A CLI security tool for analyzing, testing, and breaking JSON Web Tokens. Built for pentesters, bug bounty hunters, and security teams who need to actually understand what's inside a JWT, not just decode it.
# Option 1: pip (fastest for daily use)
pip install jwt-analyzer
jwt-analyzer audit --token-file token.txt
# Option 2: from source
git clone https://github.com/yourusername/jwt-analyzer.git
cd jwt-analyzer
pip install -r requirements.txt
python main.py audit --token-file sample_token.txt
# Option 3: Docker (best for CI/CD)
docker compose --profile audit run --rm jwt-analyzer \
audit --token-file /tokens/jwt.txtThe audit command is the main one β it runs every check and gives you a report.
Most JWT tools just decode the base64. This one actually looks for problems:
| Check | What it finds |
|---|---|
none algorithm detection |
Servers that accept unsigned tokens (CVE-2015-9235) |
| Algorithm confusion | RS256 tokens that could be forged as HS256 |
kid header injection |
SQLi / path traversal / command injection via the kid claim |
| PII / secret scanning | Credit cards, SSNs, AWS keys, passwords leaking through payloads |
| Brute-force attack | Cracks HS256 secrets using wordlists (multi-threaded) |
| JWKS verification | Validates tokens against live identity providers |
| Payload diffing | Shows exactly what changed between two tokens |
| Token forgery | Crafts new tokens with custom payloads (for auth testing) |
git clone https://github.com/yourusername/jwt-analyzer.git
cd jwt-analyzer
pip install -r requirements.txtRuntime requirements (requirements.txt):
- Python 3.9+
pyjwt[crypto] >= 2.8.0β RS/ES algorithm supportcryptography >= 41.0.0β crypto backendrequests >= 2.31.0β JWKS fetchingrich >= 13.0.0β pretty terminal outputclick >= 8.1.0β CLI frameworkpyyaml >= 6.0β config file support
Dev requirements (requirements-dev.txt):
pytest >= 7.0pytest-cov >= 4.0mypy >= 1.0ruff >= 0.1.0(linter)
docker pull yourusername/jwt-analyzer:latest
# or build locally:
docker compose buildpip install jwt-analyzer| Component | Minimum | Tested |
|---|---|---|
| Python | 3.9 | 3.14.6 |
| RAM | 256 MB | 1 GB (large wordlists) |
| Disk | 50 MB | 200 MB (with bundled wordlists) |
| OS | Any | Windows 10/11 / Ubuntu 22.04 / macOS 14 |
Windows users: Use
pyinstead ofpythonto launch scripts. Token files saved by PowerShellSet-Contentmay carry a UTF-8 BOM β the tool strips it automatically (utf-8-sigencoding), so this is transparent.
- 3.9 β 3.13: Fully supported.
- 3.14: Fully supported. Note that 3.14's
remodule enforces stricter pattern parsing, which surfaced one IPv4 regex bug in v1.2.0 (fixed in v1.2.1).
Runs every check and produces a report. This is what you want 90% of the time.
python main.py audit --token-file tokens/jwt.txtOutput:
Audit Summary
Critical: 2
High: 1
Medium: 0
Low: 0
β Critical findings - exiting with code 2
Save reports:
# JSON only (machine-readable, good for piping)
python main.py audit --token-file token.txt \
--output reports/audit-1 --format json
# HTML only (great for sharing with non-tech stakeholders)
python main.py audit --token-file token.txt \
--output reports/audit-1 --format html
# Both
python main.py audit --token-file token.txt \
--output reports/audit-1 --format bothUseful flags:
# Skip brute force if you've already tested that vector
python main.py audit --token-file token.txt --skip-bruteforce
# Use a bigger wordlist + more workers
python main.py audit --token-file token.txt \
--wordlist /usr/share/wordlists/jwt-secrets-10k.txt \
--workers 16
# Enable verbose/debug logging
python main.py --verbose audit --token-file token.txtSometimes you want just one thing. These are the building blocks:
python main.py analyze --token-file token.txtShows header, payload, and expiry status. No signature verification.
python main.py security --token-file token.txtChecks for none alg, algorithm confusion, and kid injection.
python main.py brute-force --token-file token.txt \
--wordlist utils/wordlists/common_secrets.txt \
--workers 8Tries every secret in the wordlist. Parallel by default. Only tests symmetric algorithms (HS256/384/512) β asymmetric ones use keys, not secrets.
python main.py diff --token1 old.txt --token2 new.txtPerfect for "I had role: user and now I want role: admin, what changed?"
# PEM format
python main.py verify-rsa --token-file token.txt --public-key public.pem
# JWK format (auto-detected)
python main.py verify-rsa --token-file token.txt --public-key key.jwkpython main.py verify-jwks --token-file token.txt \
--jwks-url https://auth.example.com/.well-known/jwks.json
# Skip TLS verification (debug only)
python main.py verify-jwks --token-file token.txt \
--jwks-url https://localhost:8443/jwks \
--no-verify-sslpython main.py forge \
--token-file original.txt \
--payload-file new-payload.json \
--secret mysecret \
--algorithm HS256 \
--output forged.txt \
--yes--yes flag exists because we want you to think twice.
jwt-analyzer/
βββ main.py # CLI entry point
βββ config.py # Configuration & env vars
βββ Dockerfile # Production container
βββ Dockerfile.dev # Dev container with live reload
βββ docker-compose.yml # Multi-profile orchestration
βββ Makefile # Convenience commands
βββ requirements.txt # Runtime dependencies
βββ requirements-dev.txt # Test/dev dependencies
βββ .env.example # Documented env vars
β
βββ core/
β βββ parser.py # JWT parsing & claim extraction
β βββ security.py # Vuln checks (none alg, kid injection, etc.)
β βββ verification.py # RSA/ECDSA public key verification
β βββ forgery.py # Token crafting
β βββ diff.py # Payload comparison
β βββ jwks.py # JWKS endpoint fetching + caching
β βββ sensitive_scanner.py # PII / secret detection
β βββ async_security.py # Parallel brute force
β βββ logging_config.py # JSON + console logging
β
βββ utils/
β βββ io.py # File reading helpers
β βββ report.py # HTML report generation
β βββ wordlists/
β βββ common_secrets.txt # Bundled wordlist (~10k secrets)
β
βββ plugins/ # Custom check plugins (optional)
β βββ README.md # How to write plugins
β
βββ scripts/
β βββ batch_audit.sh # Process a directory of tokens
β
βββ sample_token.txt # Try it immediately
βββ LICENSE
βββ README.md # You are here
βββ CHANGELOG.md
βββ CONTRIBUTING.md
βββ SECURITY.md
All have sensible defaults. Override when needed:
| Variable | Default | Description |
|---|---|---|
JWTANALYZER_WORDLIST_PATH |
utils/wordlists/common_secrets.txt |
Path to secrets wordlist |
JWT_ANALYZER_TIMEOUT |
300 |
Brute force timeout (seconds) |
JWT_ANALYZER_WORKERS |
8 |
Parallel workers for brute force |
JWT_ANALYZER_LOG_LEVEL |
INFO |
DEBUG / INFO / WARNING / ERROR |
JWT_ANALYZER_ENABLE_RICH |
true |
Pretty terminal output |
Example:
JWT_ANALYZER_LOG_LEVEL=DEBUG \
JWT_ANALYZER_WORKERS=16 \
python main.py audit --token-file token.txtOr use the .env.example template:
cp .env.example .env
# edit .env
set -a && source .env && set +a
python main.py audit --token-file token.txtDrop a jwt-analyzer.yaml in your working directory to override defaults
without env vars:
wordlist_path: /opt/secrets/jwt-megalist.txt
brute_force_timeout: 600
parallel_workers: 16
log_level: DEBUGThe docker-compose.yml uses profiles so nothing auto-starts. Pick the one
that matches your workflow:
| Profile | Use case |
|---|---|
audit |
One-off analysis of a single token |
ci |
Automated scans in CI/CD pipelines (respects exit codes) |
dev |
Live development with source mounted as volume |
batch |
Process a directory of tokens overnight |
jwks |
Fetch and inspect JWKS endpoint contents |
Audit a single token:
docker compose --profile audit run --rm jwt-analyzer \
audit --token-file /tokens/jwt.txt \
--output /reports/audit-1Verify against a live JWKS:
docker compose --profile jwks run --rm jwt-analyzer \
verify-jwks --token-file /tokens/jwt.txt \
--jwks-url https://auth.example.com/.well-known/jwks.jsonBatch process a directory:
# Drop JWTs (one per .txt file) into ./tokens/batch/, then:
docker compose --profile batch up
ls reports/batch/Exit codes for pipeline gating:
0= clean (only LOW/MEDIUM findings, or none)1= HIGH severity findings2= CRITICAL findings
GitHub Actions example:
- name: JWT Security Audit
run: |
docker compose --profile ci run --rm \
-v ${{ github.workspace }}/tokens:/app/tokens:ro \
jwt-analyzer audit \
--token-file /app/tokens/prod-jwt.txt \
--skip-bruteforceGitLab CI example:
jwt-audit:
stage: security
script:
- docker compose --profile ci run --rm
-v "$CI_PROJECT_DIR/tokens:/app/tokens:ro"
jwt-analyzer audit
--token-file /app/tokens/jwt.txt
--skip-bruteforce
allow_failure: falseLive-reload your source code changes without rebuilding the image:
docker compose --profile dev run --rm -it jwt-analyzer bash
# Now you're inside the container:
python main.py audit --token-file /tokens/jwt.txtA realistic pentest scenario:
# 1. Found a JWT in the app's localStorage. Dump it.
python main.py analyze --token-file captured.txt
# 2. Quick security check - 'none' alg? weak kid? confused algorithms?
python main.py security --token-file captured.txt
# 3. Try to crack the signing secret
python main.py brute-force --token-file captured.txt \
--wordlist utils/wordlists/common_secrets.txt
# 4. Got the secret. Compare a normal user token vs admin token
python main.py diff --token1 user-token.txt --token2 admin-token.txt
# β role changed from "user" to "admin", exp extended
# 5. Forge a new admin token
cat > admin-payload.json << EOF
{
"sub": "1234567890",
"role": "admin",
"exp": 9999999999
}
EOF
python main.py forge \
--token-file captured.txt \
--payload-file admin-payload.json \
--secret cracked-secret \
--output forged-admin.txt \
--yes
# 6. Full audit with report for the writeup
python main.py audit --token-file captured.txt \
--output reports/final-audit \
--format bothCustom checks can be added without modifying core code. Drop a file in
plugins/ that exposes a register() function:
# plugins/my_checks_plugin.py
class MyOrgClaimsPlugin:
name = "myorg_claims"
description = "Validates organization-specific JWT claims"
def check(self, token: str) -> dict:
# your custom check logic
...
def register():
return MyOrgClaimsPlugin()Plugins auto-load on startup. See plugins/README.md for the full API.
Your token file was saved with a non-UTF-8 encoding (usually a UTF-8 BOM from Windows PowerShell). Re-save it:
Get-Content sample_token.txt -Encoding UTF8 | Set-Content sample_token.txt -Encoding UTF8 -NoNewlineThe tool also auto-handles BOM in v1.2.1+, so this is usually a one-time fix.
Click maps function names to hyphenated command names. Use the hyphenated form:
python main.py brute-force --token-file token.txt # β
python main.py brute_force --token-file token.txt # βUse py instead:
py main.py audit --token-file token.txtYou're on Python 3.14 with an older build. Upgrade to v1.2.1 or later β this was fixed in the IPv4 pattern.
Check that --secret matches the original token's signing secret.
Run analyze on both the original and forged tokens and compare headers β
they should be identical except for alg.
Read this. Seriously.
This tool can forge tokens, crack secrets, and bypass authentication. That's the point, but it also means you can hurt people with it.
- Only test systems you have written authorization to test
- Use it for security audits, bug bounties, CTFs, your own apps, and education
- Document what you found and how (that's what
--output reports/is for) - Follow responsible disclosure for any vulns you discover
- Don't run this against systems you don't own or have explicit permission for
- Don't use it to access accounts that aren't yours
- Don't be the reason computer fraud laws exist
Unauthorized access to computer systems is illegal in most jurisdictions
(CFAA in the US, Computer Misuse Act in the UK, similar laws everywhere).
The --yes flag on forge exists because we want you to think twice.
Found a security bug in jwt-analyzer itself? Please email [email protected] (do not open a public GitHub issue). See SECURITY.md for our full disclosure policy.
Contributions welcome! See CONTRIBUTING.md for guidelines.
Quick checklist for PRs:
- New functionality is documented in the README
- Entry added to CHANGELOG.md under
[Unreleased] - No new hardcoded paths or secrets
- Code passes
ruff checkandmypyif you ran them locally - Plugin additions follow the pattern in
plugins/README.md
MIT β see LICENSE for the full text.
In short: do what you want, just don't blame us if something goes wrong, and keep the copyright notice.
- The OWASP JWT cheat sheet β for the vulnerability patterns
- SecLists β bundled wordlists
- The PyJWT maintainers β solid crypto library
- Everyone who's reported bugs and contributed fixes