JWT (JSON Web Token) Attacks
1. Definition
JWT (JSON Web Token) is a compact, URL-safe token format used for authentication and information exchange. JWT attacks exploit weaknesses in how applications create, validate, or handle these tokens.
A JWT consists of three parts: header.payload.signature
Common JWT vulnerabilities allow attackers to:
- Forge valid tokens without knowing the secret
- Escalate privileges by modifying token claims
- Bypass authentication entirely
- Impersonate other users
2. Technical Explanation
JWT Structure:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. <- Header (Base64)
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4ifQ. <- Payload (Base64)
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c <- SignatureHeader: Specifies the algorithm (e.g., HS256, RS256, none) Payload: Contains claims (user ID, role, expiration) Signature: Verifies the token was not tampered with
Common JWT Attack Types:
Algorithm Confusion (alg=none): Some libraries accept
"alg": "none", allowing unsigned tokens.Algorithm Switching (RS256 to HS256): Tricking the server into using the public key as an HMAC secret.
Weak Secret Brute Force: Cracking weak HMAC secrets offline.
JWT Header Injection (jku/x5u): Pointing to attacker-controlled key servers.
Claim Tampering: Modifying payload without proper signature validation.
3. Attack Flow (Algorithm Confusion)
sequenceDiagram
participant Attacker
participant Server as Web Server
participant Library as JWT Library
Note over Server: Server uses RS256<br/>Public/Private key pair
Attacker->>Attacker: Obtain servers public key
Attacker->>Attacker: Create forged JWT<br/>Header: alg=HS256<br/>Payload: admin=true
Attacker->>Attacker: Sign with public key<br/>treating it as HMAC secret
Attacker->>Server: Request with forged JWT
Server->>Library: Verify JWT
Library->>Library: Read alg=HS256 from header<br/>Use public key as HMAC secret<br/>Signature matches!
Library-->>Server: Token valid
Server-->>Attacker: Access granted as admin4. Real-World Case Study: Auth0 Algorithm Confusion (2015)
Target: Auth0 JWT library users. Vulnerability Class: Algorithm Confusion (CVE-2015-9235).
The Vulnerability: Auth0’s jsonwebtoken library for Node.js had a critical flaw. When verifying tokens signed with RSA (RS256), if an attacker changed the algorithm to HS256 in the header, the library would use the RSA public key as the HMAC secret.
Why This Worked:
- RS256 uses asymmetric encryption: private key signs, public key verifies.
- HS256 uses symmetric encryption: same secret for both signing and verification.
- Public keys are often… public (in JWKS endpoints, certificates, etc.).
- The library trusted the
algheader from the token itself.
The Attack:
// Original token (RS256)
// Header: {"alg": "RS256", "typ": "JWT"}
// Signed with server's private key
// Attacker's forged token (HS256)
// Header: {"alg": "HS256", "typ": "JWT"}
// Payload: {"user": "admin", "role": "superuser"}
// Signed with server's PUBLIC key as HMAC secretImpact: Any application using the vulnerable library could have their authentication completely bypassed. This affected thousands of applications using Auth0’s library.
5. Detailed Defense Strategies
A. Explicit Algorithm Specification
Never trust the algorithm specified in the token header.
// Bad: Algorithm from token header
jwt.verify(token, secret);
// Good: Explicit algorithm whitelist
jwt.verify(token, secret, { algorithms: ['HS256'] });
// Good: For RSA
jwt.verify(token, publicKey, { algorithms: ['RS256'] });B. Strong Secrets
For HMAC-based algorithms, use cryptographically strong secrets.
- Minimum Length: 256 bits (32 bytes) for HS256.
- Random Generation: Use cryptographic random generators.
- No Dictionary Words: Avoid guessable passwords.
# Generate strong secret
openssl rand -base64 32Weak secrets can be cracked offline:
# Attacker can brute force weak secrets
hashcat -m 16500 jwt.txt wordlist.txtC. Proper Key Management
- Separate Keys: Different keys for different environments.
- Key Rotation: Regular rotation with overlap period.
- Secure Storage: Use HSM or secrets management services.
D. Validate All Claims
Don’t just verify the signature—validate the payload.
jwt.verify(token, secret, {
algorithms: ['HS256'],
issuer: 'https://myapp.com', // Validate issuer
audience: 'https://api.myapp.com', // Validate audience
clockTolerance: 30, // Allow 30s clock skew
maxAge: '1h' // Reject tokens older than 1 hour
});E. Short Expiration Times
Minimize the window of opportunity for stolen tokens.
- Access Tokens: 15 minutes or less.
- Refresh Tokens: Hours to days, with secure storage.
- Implement Token Revocation: Maintain a blocklist for compromised tokens.
F. Avoid Sensitive Data in Payload
JWTs are signed, not encrypted (unless using JWE).
// Bad: Sensitive data in JWT
{
"user_id": 123,
"credit_card": "4111-1111-1111-1111", // Never do this!
"ssn": "123-45-6789" // Never do this!
}
// Good: Only non-sensitive identifiers
{
"user_id": 123,
"role": "user",
"exp": 1609459200
}G. Use Asymmetric Algorithms for Distributed Systems
When multiple services need to verify tokens:
- RS256/RS384/RS512: RSA signatures
- ES256/ES384/ES512: ECDSA signatures
Only the auth server has the private key; all services can verify with the public key.
H. Implement JTI (JWT ID) for Replay Protection
{
"jti": "unique-token-id-abc123", // Unique identifier
"user_id": 123,
"exp": 1609459200
}Track used jti values to prevent token replay.
