Comprehensive JWT Security Guide#
A practitioner’s reference for JSON Web Token security – vulnerabilities, exploitation techniques, attack vectors, implementation flaws, and defense strategies. Covers algorithm confusion, signature bypass, header injection, key confusion, library-specific issues, cryptographic attacks, attack chaining, and secure implementation patterns. Compiled from 42 research sources.
Table of Contents#
- Fundamentals
- JWT Structure & Components
- Algorithm Confusion Attacks
- Signature Bypass Techniques
- Header Manipulation
- Payload Tampering
- Cryptographic & Protocol-Level Attacks
- Library-Specific Vulnerabilities
- Attack Chaining
- Implementation Security
- Attack Methodology
- Secure Development Practices
1. Fundamentals#
JWT Overview#
| Component | Purpose | Security Relevance |
|---|
| Header | Algorithm and token type declaration | Algorithm confusion vector |
| Payload | Claims and data | Authorization decisions |
| Signature | Integrity and authenticity proof | Bypass target |
Common Use Cases#
| Application | JWT Role | Attack Impact |
|---|
| Authentication | Identity assertion | Account takeover |
| Authorization | Permission claims | Privilege escalation |
| Information Exchange | Secure data transmission | Information disclosure |
| API Access | Bearer token | Unauthorized access |
| Microservices | Stateless session | Cross-service token reuse |
JWS vs JWE#
| Format | Purpose | Security Consideration |
|---|
| JWS (JSON Web Signature) | Signed tokens – integrity and authenticity | Most common; payload is readable (base64url), not encrypted |
| JWE (JSON Web Encryption) | Encrypted tokens – confidentiality | Payload confidential; format confusion with JWS possible |
2. JWT Structure & Components#
Token Anatomy#
JWT STRUCTURE:
Header.Payload.Signature
Example:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
| Parameter | Description | Security Implications |
|---|
alg | Signature algorithm | Algorithm confusion attacks |
typ | Token type | Type confusion, JWS/JWE format confusion |
kid | Key identifier | Path traversal, SQL injection, command injection |
jku | JWK Set URL | SSRF, URL manipulation, external key loading |
jwk | Embedded JSON Web Key | Key injection – attacker embeds own public key |
x5u | X.509 URL | Certificate injection, SSRF |
x5c | X.509 Certificate Chain | Certificate substitution |
Standard Claims#
| Claim | Purpose | Attack Vectors |
|---|
iss (Issuer) | Token origin | Issuer spoofing, substitution attacks |
sub (Subject) | Token subject | User ID manipulation |
aud (Audience) | Intended recipient | Audience bypass, cross-service token reuse |
exp (Expiration) | Token lifetime | Expiry bypass, token persistence |
iat (Issued At) | Issue timestamp | Replay attacks |
nbf (Not Before) | Activation time | Timing bypass |
3. Algorithm Confusion Attacks#
Attack Mechanism#
ALGORITHM CONFUSION FLOW:
1. Server expects RS256 (RSA + SHA256)
2. Attacker changes alg to HS256 (HMAC + SHA256)
3. Server's public RSA key used as HMAC secret
4. Attacker generates valid HMAC signature
5. Server verifies with same key -> bypass
Vulnerable Algorithm Transitions#
| Original Algorithm | Confused Algorithm | Attack Method |
|---|
| RS256/RS384/RS512 | HS256/HS384/HS512 | Public key as HMAC secret |
| ES256/ES384/ES512 | HS256/HS384/HS512 | Public key as HMAC secret |
| PS256/PS384/PS512 | HS256/HS384/HS512 | Public key as HMAC secret |
| Any Algorithm | none | No signature verification |
Exploitation Techniques#
| Attack Vector | Payload Example | Impact |
|---|
| RSA->HMAC | {"alg":"HS256"} + HMAC(payload, rsa_public_key) | Complete authentication bypass |
| Algorithm None | {"alg":"none"} + unsigned token | Total signature bypass |
| None Variants | {"alg":"NoNe"}, {"alg":"NONE"}, {"alg":"nOnE"} | Blacklist bypass on case-insensitive checks |
| Weak Algorithm | {"alg":"HS256"} with known/weak secret | Signature forgery |
CVE-2024-54150: cjwt C Library#
A real-world algorithm confusion in the xmidt-org/cjwt C library. The cjwt_decode() function reads the algorithm from the JWT header without requiring the caller to specify an expected algorithm. The verification function jws_verify_signature() dispatches based on the header-controlled alg value, allowing an attacker to sign with HMAC using the server’s RSA public key.
Key code review red flags:
- Single decode function for both HMAC and RSA with identical call signatures
- Algorithm determined by parsing untrusted JWT header (
cJSON_GetObjectItemCaseSensitive) - No API parameter for caller to restrict accepted algorithms
4. Signature Bypass Techniques#
Direct Signature Attacks#
| Method | Technique | Requirements |
|---|
| Empty Signature | Remove signature section | Vulnerable parser |
| Signature Stripping | Modify to alg: "none" | Missing algorithm validation |
| Brute Force | HMAC secret guessing via hashcat | Weak secret key |
| Dictionary Attack | Common secret wordlists | Predictable secrets |
| decode() vs verify() | Application uses decode-only function | Developer misunderstanding |
Brute Forcing HMAC Secrets#
HMAC BRUTE FORCE WITH HASHCAT:
hashcat -a 0 -m 16500 <JWT_TOKEN> /path/to/jwt.secrets.list
Mode 16500 = JWT signature cracking
Use --show to display previously cracked results
Wordlists: SecLists/Passwords/scraped-JWT-secrets.txt
Key insight (RFC 8725): Human-memorizable passwords MUST NOT be directly used as HMAC keys. Minimum 256 bits of entropy required for HS256.
Signature Validation Flaws#
COMMON VALIDATION ERRORS:
├── Missing Algorithm Verification
│ ├── Accept any algorithm in header
│ ├── No algorithm allowlist
│ └── Default to insecure algorithms
├── Improper Key Handling
│ ├── Same key for multiple algorithms
│ ├── Public key reuse
│ └── Key confusion attacks
├── Logic Bypasses
│ ├── Empty signature acceptance
│ ├── Null signature handling
│ └── Exception swallowing
└── decode() vs verify()
├── Using jwt.decode() instead of jwt.verify()
├── Decoding without signature check
└── Trusting decoded payload as authenticated
Audience Verification Bypass (Go jwt-go)#
The dgrijalva/jwt-go library’s VerifyAudience function accepted empty string ("") as a valid audience value, allowing audience verification to succeed when it should fail. Fix was available in v4.0.0-preview1 but not merged to master, leaving most Go projects vulnerable.
Key Identifier (kid) Attacks#
| Attack | Payload | Effect |
|---|
| Path Traversal | "kid":"../../dev/null" | Sign with empty/known file content |
| Path Traversal | "kid":"../../../public.key" | Use predictable file as key |
| URL Injection | "kid":"http://attacker.com/key" | External key loading |
| SQL Injection | "kid":"' UNION SELECT 'known-secret'--" | Return attacker-controlled key from DB |
| Command Injection | "kid":";cat /etc/passwd" | Code execution |
Remediation pattern (kid path traversal):
// VULNERABLE: direct file path from kid
const keyPath = `/keys/${kid}`;
const key = fs.readFileSync(keyPath);
// SECURE: allowlist mapping
const allowedKeys = {
'key-2024-01': '/secure/keys/2024-01.pem',
'key-2024-02': '/secure/keys/2024-02.pem'
};
if (!allowedKeys.hasOwnProperty(kid)) throw new Error('Invalid key');
const key = fs.readFileSync(allowedKeys[kid]);
Remediation pattern (kid SQL injection):
// VULNERABLE: string concatenation
String query = "SELECT key FROM keys WHERE kid = '" + kidValue + "'";
// SECURE: parameterized query
PreparedStatement stmt = conn.prepareStatement("SELECT key FROM keys WHERE kid = ?");
stmt.setString(1, kidValue);
JWK Set URL (jku) Manipulation#
JKU ATTACK CHAIN:
1. Attacker controls jku parameter
2. Points to malicious JWK Set (hosted on attacker server)
3. Server fetches attacker's keys without URL validation
4. Token validates with attacker's key
5. Complete authentication bypass
SSRF CLASSIFICATION: jku fetching = server-side request to attacker URL
Mitigations:
- Disable
jku parameter support entirely (recommended) - Strict URL allowlisting (full URL, not just domain)
- Disable HTTP redirects when fetching JWKS
- Validate URL before any fetch operation
JWK INJECTION ATTACK:
1. Attacker generates own RSA key pair
2. Modifies JWT payload (e.g., role: "admin")
3. Adds own public key in jwk header parameter
4. Sets kid to match the embedded jwk
5. Signs token with own private key
6. Server uses embedded public key to verify -> bypass
REQUIRES: Server accepts jwk parameter without key allowlisting
X.509 Certificate Attacks#
| Vector | Description | Mitigation |
|---|
| Certificate Injection | Supply malicious certificate via x5u | Strict URL validation |
| Self-Signed Certs | Use untrusted certificates | Certificate chain validation |
| Certificate Confusion | Mix certificate types | Explicit algorithm binding |
| x5c Chain Injection | Embed attacker cert chain in header | Validate against trusted root CAs |
6. Payload Tampering#
Claim Manipulation#
| Claim | Attack Example | Impact |
|---|
User ID (sub) | Change to admin user | Horizontal privilege escalation |
Role (role) | Elevate to administrator | Vertical privilege escalation |
Expiration (exp) | Extend lifetime | Persistent access |
Audience (aud) | Change target application | Cross-application attacks (substitution) |
| Username | Change "username":"johndoe" to "username":"admin" | Identity spoofing |
Business Logic Bypasses#
PAYLOAD ATTACK PATTERNS:
├── Privilege Escalation
│ ├── Role claim modification (user -> admin)
│ ├── Permission array tampering
│ └── Group membership changes
├── Time-Based Attacks
│ ├── Expiry extension (exp)
│ ├── Not-before bypass (nbf)
│ └── Issued-at manipulation (iat)
├── Cross-Application Attacks (Substitution)
│ ├── Audience switching (aud)
│ ├── Issuer spoofing (iss)
│ ├── Token reuse across services
│ └── Cross-JWT confusion (RFC 8725 Section 2.8)
└── Indirect Server Attacks
├── Claims used in LDAP lookups (injection)
├── Claims used in database queries (SQLi)
└── Claims containing URLs (SSRF)
7. Cryptographic & Protocol-Level Attacks#
Elliptic Curve Invalid Curve Attacks#
JOSE libraries that fail to validate elliptic curve inputs during ECDH-ES key agreement are vulnerable to invalid curve point attacks. An attacker sends JWEs with invalid curve points and observes cleartext outputs to recover the recipient’s private key.
| Algorithm | Attack | Mitigation |
|---|
| ECDH-ES | Invalid curve point injection | Validate points per NIST SP 800-56A Section 5.6.2.3.4 |
| ES256 | Psychic Signatures (CVE-2022-21449) | Java EC signature with zero values accepted as valid |
JWE-Specific Attacks#
| Attack | Description | Mitigation |
|---|
| JWE Decompression Bomb | Compressed JWE payload expands to exhaust memory | Limit decompression output size |
| Compression Oracle | Ciphertext length leaks plaintext information when compression enabled | Avoid compression before encryption (RFC 8725 Section 3.6) |
| JWS/JWE Format Confusion | Verifier doesn’t check if received JWT is JWS vs JWE; successful decryption treated as signature validation (CVE-2023-51774) | Explicitly check JWT format type |
Hash Iteration DoS#
Libraries that support password-based encryption (PBES2) may accept unreasonable iteration counts, causing CPU exhaustion when processing a maliciously crafted token.
| Parameter | Risk | Mitigation |
|---|
p2c (PBES2 count) | Attacker sets extremely high iteration count | Limit maximum hash iteration count |
Bleichenbacher Attack on RSA-PKCS1#
RSA-PKCS1 v1.5 encryption algorithms are vulnerable to Bleichenbacher’s chosen-ciphertext attack (padding oracle). Prefer RSAES-OAEP.
8. Library-Specific Vulnerabilities#
Historical Vulnerabilities#
| Library | CVE | Vulnerability | Impact |
|---|
| node-jsonwebtoken | CVE-2015-9235 | Algorithm confusion | Authentication bypass |
| node-jsonwebtoken | CVE-2022-23529 | RCE via malicious secretOrPublicKey object | Remote code execution (retracted – requires caller misuse) |
| pyjwt | CVE-2017-11424 | Key confusion | Signature verification bypass |
| php-jwt | CVE-2021-46743 | Algorithm substitution | Authentication bypass |
| jose4j | CVE-2023-51775 | Algorithm confusion | Token forgery |
| xmidt-org/cjwt (C) | CVE-2024-54150 | Algorithm confusion – no caller-specified algorithm | Authentication bypass via HMAC/RSA key confusion |
| dgrijalva/jwt-go (Go) | SNYK-GOLANG-596515 | Audience verification bypass (empty string accepted) | Access restriction bypass |
| Java SE | CVE-2022-21449 | “Psychic Signatures” – EC zero-value signature accepted | Signature validation bypass |
| nimbus-jose-jwt | CVE-2023-51774 | JWS/JWE format confusion | Authentication bypass |
Framework Integration Issues#
FRAMEWORK VULNERABILITIES:
├── Express.js/Node.js
│ ├── jsonwebtoken algorithm confusion
│ ├── jwt.decode() vs jwt.verify() misuse
│ ├── Middleware bypass techniques
│ ├── CVE-2022-23529 (secretOrPublicKey object injection)
│ └── Error handling flaws
├── Django/Python
│ ├── PyJWT verification bypasses
│ ├── Algorithm None attacks
│ ├── Key handling issues
│ └── Flask-JWT-Extended misconfiguration
├── Spring Boot/Java
│ ├── JJWT library flaws
│ ├── Algorithm validation bypass
│ ├── CVE-2022-21449 (EC Psychic Signatures)
│ ├── Token sidejacking defenses (OWASP)
│ └── Key injection vulnerabilities
├── ASP.NET/C#
│ ├── System.IdentityModel flaws
│ ├── Algorithm confusion
│ └── Certificate validation bypass
└── Go
├── dgrijalva/jwt-go audience bypass
├── Master branch vs tagged release divergence
└── VerifyAudience empty string acceptance
9. Attack Chaining#
JWT Bypass to RCE#
CTF EXAMPLE: JWT None Algorithm -> Admin Panel -> SSTI -> RCE
Chain:
1. Decode JWT, identify role claim ("user")
2. Set alg to "none", change role to "admin", strip signature
3. Access admin panel with forged token
4. Discover input field vulnerable to Jinja2 SSTI
5. Execute RCE via Python class hierarchy traversal:
{% for x in ().__class__.__base__.__subclasses__() %}
{% if "warning" in x.__name__ %}
{{x()._module.__builtins__["__import__"]("os").popen('whoami').read()}}
{% endif %}
{% endfor %}
6. Read flag from filesystem
Impact: JWT auth bypass -> privilege escalation -> code execution
Common JWT Attack Chains#
| Initial Vulnerability | Escalation | Final Impact |
|---|
| JWT none algorithm | Admin panel access | Privilege escalation |
| JWT kid path traversal | Read sensitive files | Information disclosure / auth bypass |
| JWT kid SQL injection | Extract signing keys from DB | Full token forgery |
| JWT jku injection | SSRF to internal services | Internal network access |
| JWT weak secret + brute force | Forge tokens for any user | Account takeover |
| JWT alg confusion + claim modification | Admin role assignment | Full application compromise |
10. Implementation Security#
Secure JWT Verification#
| Security Control | Implementation | Common Mistakes |
|---|
| Algorithm Validation | Strict allowlist (whitelist, not blacklist) | Accept any algorithm; blacklist none with case-sensitive check |
| Key Management | Rotate regularly, separate keys per algorithm | Reuse across algorithms |
| Signature Verification | Mandatory for all tokens; use verify() not decode() | Optional or bypassable; using decode-only functions |
| Claim Validation | Validate all critical claims server-side | Trust payload data |
| Format Type Check | Explicitly verify JWS vs JWE format | Accept any JWT structure |
Token Sidejacking Defense (OWASP)#
TOKEN FINGERPRINTING:
1. On authentication, generate random string (50 bytes, SecureRandom)
2. Send raw value as hardened cookie:
__Secure-Fgp=<value>; SameSite=Strict; HttpOnly; Secure
3. Store SHA-256 hash of value in JWT claim (userFingerprint)
4. On each request:
a. Read fingerprint cookie
b. Hash it with SHA-256
c. Compare to JWT claim
d. Reject token if mismatch
BENEFIT: Even if JWT is stolen, attacker lacks the HttpOnly cookie
NOTE: Avoid IP-based fingerprinting (changes on mobile networks, GDPR concerns)
Token Revocation Strategies#
| Strategy | Mechanism | Trade-off |
|---|
| Short-lived tokens | Set exp to 15 minutes max | Frequent re-authentication |
| Token denylist (DB) | SHA-256 digest stored in revocation table | Reintroduces server state |
| Refresh token rotation | Issue new access token via refresh endpoint | Refresh token must be secured |
| Cookie clearing on logout | Clear JWT from sessionStorage + cookie | Only effective if user initiates logout |
Key Management Best Practices#
SECURE KEY PRACTICES:
├── Key Generation
│ ├── Cryptographically random (SecureRandom, os.urandom)
│ ├── Sufficient entropy (256+ bits for HMAC)
│ ├── Algorithm-specific requirements
│ └── Never use human-memorizable passwords as HMAC keys
├── Key Storage
│ ├── Hardware security modules (HSM)
│ ├── Cloud KMS (AWS KMS, Azure Key Vault, GCP KMS)
│ ├── Environment variables (development only)
│ └── Never hardcode in source code
├── Key Rotation
│ ├── Regular rotation schedule
│ ├── Grace periods for old keys (support both during transition)
│ └── Emergency rotation procedures
├── Key Distribution
│ ├── Secure channels only (HTTPS)
│ ├── JWK Set endpoints (/.well-known/jwks.json)
│ └── Certificate-based PKI
└── Key-Algorithm Binding
├── Each key used with exactly one algorithm
├── Separate keys for signing vs encryption
└── Validated at cryptographic operation time
11. Attack Methodology#
Reconnaissance Phase#
| Target | Information Gathering | Tools |
|---|
| JWT Structure | Token analysis, header/payload decode | JWT.io, jwt_tool, CyberChef |
| Algorithm Detection | Header inspection | Burp Suite, manual analysis |
| Key Discovery | Public key extraction, JWKS endpoints | Certificate analysis, /.well-known/jwks.json |
| Implementation Details | Error message analysis, library fingerprinting | Fuzzing, invalid tokens |
| Framework Detection | Wappalyzer, BuiltWith | Technology stack identification |
Exploitation Workflow#
ATTACK SEQUENCE:
├── Token Acquisition
│ ├── Login with valid credentials
│ ├── Social engineering
│ └── Token leakage (logs, URLs, Referer headers)
├── Token Analysis
│ ├── Decode header and payload (base64url)
│ ├── Identify critical claims (role, sub, aud)
│ ├── Determine algorithm
│ └── Check for optional header params (kid, jku, jwk)
├── Vulnerability Testing
│ ├── Algorithm none (with case variants)
│ ├── Algorithm confusion (RS256 -> HS256)
│ ├── Signature removal / empty signature
│ ├── Weak secret brute force (hashcat -m 16500)
│ ├── kid injection (path traversal, SQLi)
│ ├── jku/jwk injection
│ ├── Claim manipulation (role, sub, exp)
│ └── decode() vs verify() check
├── Escalation
│ ├── Forge admin tokens
│ ├── Chain with other vulns (SSTI, SSRF, SQLi)
│ └── Cross-service token reuse
└── Impact Assessment
├── Authentication bypass
├── Privilege escalation
├── Data access
└── Remote code execution (via chaining)
| Tool | Purpose | Features |
|---|
| jwt_tool | JWT manipulation & testing | Algorithm attacks, claim fuzzing, automated scanning |
| Burp JWT Editor | Token editing & signing | Real-time editing, key generation, re-signing |
| JWT Scanner (Burp) | Automated vulnerability scanning | None algorithm, CVE-2022-21449, key confusion, public key forging |
| JOSEPH (Burp) | JOSE pentesting helper | Bleichenbacher MMA, key confusion, signature exclusion |
| hashcat (-m 16500) | HMAC secret brute force | Dictionary/brute force attacks on JWT signing keys |
| c-jwt-cracker | HMAC brute force (C) | Fast native cracking |
| JWT2John | Password cracking prep | Extract JWT for John the Ripper |
| jwtXploiter | JWT security testing | Multiple attack vectors |
| jwt-pwn | JWT security testing scripts | Python-based testing suite |
| Snyk | SAST for JWT misuse | Detects jwt.decode() misuse, vulnerable library versions |
12. Secure Development Practices#
Implementation Checklist#
| Security Control | Verification | Risk Level |
|---|
| Algorithm Allowlist | Explicitly define allowed algorithms (whitelist) | Critical |
| Signature Verification | Mandatory for all tokens; use verify() | Critical |
| Key Management | Secure generation, storage, rotation; 256+ bit entropy | Critical |
| Key-Algorithm Binding | Each key used with exactly one algorithm | Critical |
| Claim Validation | Validate iss, aud, exp, nbf, sub | High |
| Format Type Check | Verify JWS vs JWE explicitly | High |
| Header Parameter Restrictions | Reject/ignore jku, jwk, x5u unless explicitly needed | High |
| kid Validation | Allowlist or parameterized lookups only | High |
| Token Lifetime | Short expiration (15 min access tokens) | High |
| Token Fingerprinting | Bind tokens to client context (hardened cookie) | Medium |
| Error Handling | No information leakage in error responses | Medium |
| Token Revocation | Implement denylist for logout support | Medium |
| Compression | Never compress before encryption | Medium |
| HTTPS Transport | Tokens transmitted only over TLS | Medium |
RFC 8725 Best Practices Summary#
RFC 8725 / draft-sheffer-oauth-rfc8725bis KEY REQUIREMENTS:
├── Algorithm Verification
│ ├── Libraries MUST let caller specify supported algorithms
│ ├── MUST NOT use algorithms not in the specified set
│ ├── Each key MUST be used with exactly one algorithm
│ └── MUST be checked at cryptographic operation time
├── Cryptographic Hygiene
│ ├── Validate all cryptographic operations (including nested JWTs)
│ ├── Validate elliptic curve inputs (invalid curve attacks)
│ ├── Keys MUST have sufficient entropy (256+ bits)
│ ├── Avoid compression before encryption
│ └── Use UTF-8 encoding exclusively
├── Claim Validation
│ ├── Validate issuer and subject
│ ├── Use and validate audience
│ ├── Do NOT trust received claims for server-side operations
│ └── Use explicit typing (typ header) to prevent cross-JWT confusion
├── Format Safety (RFC 8725bis additions)
│ ├── Limit hash iteration count (PBES2 p2c parameter)
│ ├── Check JWT format type (JWS vs JWE)
│ └── Limit JWE decompression size
└── General
├── Use RSAES-OAEP instead of RSA-PKCS1 v1.5
├── Implement ECDSA with deterministic nonces (RFC 6979)
└── Use mutually exclusive validation rules for different JWT types
Framework-Specific Guidance#
SECURE IMPLEMENTATION PATTERNS:
├── Node.js/Express
│ ├── Use jsonwebtoken with explicit algorithms option
│ ├── ALWAYS use jwt.verify(), NEVER jwt.decode() for auth
│ ├── Implement proper error handling (no stack traces)
│ ├── Validate all claims explicitly
│ └── Keep jsonwebtoken >= v9.0.0
├── Python/Django/Flask
│ ├── PyJWT with algorithms parameter (whitelist)
│ ├── Always verify signature before trusting claims
│ ├── Custom middleware for validation
│ ├── Secure key storage (environment variables / KMS)
│ └── Flask-JWT-Extended with secure cookie configuration
├── Java/Spring Boot
│ ├── Spring Security JWT support with algorithm validation
│ ├── Token sidejacking with fingerprint cookies (OWASP pattern)
│ ├── Token denylist for revocation
│ ├── JWK Set endpoints for key distribution
│ ├── Ensure JDK patched for CVE-2022-21449
│ └── Use auth0/java-jwt or nimbus-jose-jwt (latest)
├── .NET/ASP.NET Core
│ ├── Microsoft.IdentityModel.JsonWebTokens
│ ├── TokenValidationParameters (strict configuration)
│ └── Strict algorithm validation
└── Go
├── Use golang-jwt/jwt (not dgrijalva/jwt-go)
├── Validate audience explicitly
└── Pin to tagged releases, not master branch
Security Testing Strategy#
| Test Category | Test Cases | Expected Result |
|---|
| Algorithm Tests | none (all case variants), HS256<->RS256, invalid | Reject all invalid algorithms |
| Signature Tests | Missing, empty, invalid, decode-only | Reject all invalid signatures |
| Claim Tests | Expired, wrong audience, wrong issuer, missing | Validate all critical claims |
| Header Tests | kid injection (traversal, SQLi), jku/jwk manipulation | Reject malicious parameters |
| Key Tests | Weak HMAC secret, brute force, public key confusion | Resist all key attacks |
| Format Tests | JWE sent where JWS expected, nested JWT | Explicit format validation |
| Chaining Tests | JWT bypass + secondary exploitation | Defense in depth at each layer |
Key Takeaways#
- Algorithm Validation: Always use explicit algorithm allowlists – never blacklists (case-variant bypass)
- Signature Verification: Use
verify(), never decode() for authentication decisions - Key Management: Use separate keys per algorithm, 256+ bits entropy, never human-memorable passwords
- Claim Validation: Validate all security-relevant claims (iss, aud, exp, nbf, sub)
- Header Restrictions: Reject or strictly validate jku, jwk, kid, x5u parameters
- Format Awareness: Explicitly check JWS vs JWE format to prevent confusion attacks
- Token Binding: Use fingerprint cookies to mitigate token sidejacking
- Library Updates: Keep JWT libraries current; review CVE history before adopting new libraries
- Defense in Depth: JWT bypass alone may not be critical; prevent secondary exploitation (SSTI, SQLi, SSRF)
- RFC 8725 Compliance: Follow IETF Best Current Practices as minimum security baseline
This guide compiles practical JWT security knowledge from 42 research sources. Stay updated with emerging JWT attack techniques, library vulnerabilities, and RFC 8725bis updates.