| name | jwt-misuse-anti-pattern |
| description | Security anti-pattern for JWT misuse vulnerabilities (CWE-287). Use when generating or reviewing code that creates, validates, or uses JSON Web Tokens. Detects 'none' algorithm attacks, weak secrets, sensitive data in payloads, and missing expiration. |
JWT Misuse Anti-Pattern
Severity: High
Summary
JSON Web Tokens (JWTs) are frequently misused in AI-generated code, creating critical vulnerabilities. Common flaws include accepting the "none" algorithm, weak secrets, sensitive data in payloads, and missing expiration. These enable authentication bypass, token forgery, and sensitive data exposure.
The Anti-Patterns and Solutions
1. Algorithm Confusion ("none" Algorithm Attack)
Critical vulnerability where library accepts any algorithm in token header. Attacker changes algorithm to "none" and removes signature, bypassing all cryptographic validation.
BAD Code Example
import jwt
def verify_jwt_vulnerable(token, secret_key):
try:
decoded = jwt.decode(token, secret_key, algorithms=None)
return decoded
except jwt.PyJWTError as e:
print(f"JWT verification failed: {e}")
return None
GOOD Code Example
import jwt
def verify_jwt_secure(token, secret_key):
try:
decoded = jwt.decode(token, secret_key, algorithms=["HS256", "RS256"])
return decoded
except jwt.PyJWTError as e:
print(f"JWT verification failed: {e}")
return None
2. Weak Secret
Weak, predictable, or hardcoded secrets for symmetric signing algorithms (HS256) enable attackers to brute-force secrets and forge valid tokens.
BAD Code Example
import jwt
JWT_SECRET = "secret123"
def create_jwt(user_id):
payload = {"user_id": user_id}
return jwt.encode(payload, JWT_SECRET, algorithm="HS256")
GOOD Code Example
import jwt
import os
JWT_SECRET = os.environ.get("JWT_SECRET")
def initialize():
if not JWT_SECRET or len(JWT_SECRET) < 32:
raise ValueError("JWT_SECRET must be at least 256 bits (32 chars) for HS256")
def create_jwt_asymmetric(user_id, private_key):
payload = {"sub": user_id}
return jwt.encode(payload, private_key, algorithm="RS256")
3. Sensitive Data in Payload
JWT payload is Base64Url-encoded, not encrypted. Anyone intercepting the token can decode and read it. Storing PII, passwords, or internal data in payload creates major security risk.
BAD Code Example
import jwt
def create_jwt_with_pii(user, secret_key):
payload = {
"user_id": user.id,
"email": user.email,
"ssn": user.social_security_number,
"password_hash": user.password_hash
}
return jwt.encode(payload, secret_key, algorithm="HS256")
GOOD Code Example
import jwt
import time
def create_jwt_secure(user, secret_key):
payload = {
"sub": user.id,
"iat": int(time.time()),
"exp": int(time.time()) + 3600,
"role": user.role
}
return jwt.encode(payload, secret_key, algorithm="HS256")
Language-Specific Examples
JavaScript/Node.js:
const jwt = require('jsonwebtoken');
const SECRET = 'mysecret';
function verifyToken(token) {
return jwt.verify(token, SECRET);
}
function createToken(user) {
return jwt.sign({
id: user.id,
email: user.email,
password: user.passwordHash,
ssn: user.ssn
}, SECRET);
}
const jwt = require('jsonwebtoken');
const SECRET = process.env.JWT_SECRET;
if (!SECRET || SECRET.length < 32) {
throw new Error('JWT_SECRET must be at least 256 bits');
}
function verifyToken(token) {
return jwt.verify(token, SECRET, {
algorithms: ['HS256'],
maxAge: '1h'
});
}
function createToken(user) {
const now = Math.floor(Date.now() / 1000);
return jwt.sign({
sub: user.id,
iat: now,
exp: now + 3600,
role: user.role
}, SECRET, {
algorithm: 'HS256'
});
}
Java:
import io.jsonwebtoken.*;
public class JwtService {
private static final String SECRET = "secret123";
public Claims verifyToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
}
public String createToken(User user) {
return Jwts.builder()
.setSubject(user.getId())
.claim("email", user.getEmail())
.claim("ssn", user.getSsn())
.signWith(SignatureAlgorithm.HS256, SECRET)
.compact();
}
}
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
import java.util.Date;
public class SecureJwtService {
private static final String SECRET_KEY = System.getenv("JWT_SECRET");
private static final Key KEY = Keys.hmacShaKeyFor(SECRET_KEY.getBytes());
public Claims verifyToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(KEY)
.requireAlgorithm("HS256")
.build()
.parseClaimsJws(token)
.getBody();
}
public String createToken(User user) {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
Date expiry = new Date(nowMillis + 3600000);
return Jwts.builder()
.setSubject(user.getId())
.setIssuedAt(now)
.setExpiration(expiry)
.claim("role", user.getRole())
.signWith(KEY, SignatureAlgorithm.HS256)
.compact();
}
}
Detection
- Find algorithm confusion vulnerabilities: Grep for unsafe jwt.decode calls:
rg 'jwt\.decode.*algorithms\s*=\s*(None|null|\[\])'
rg 'jwt\.decode' | rg -v 'algorithms=' (missing explicit algorithm)
rg 'verify.*false|verify:\s*false' --type js (disabled verification)
- Identify weak secrets: Search for hardcoded JWT keys:
rg 'JWT_SECRET.*=.*["\'][^"\']{1,16}["\']' (short secrets < 32 chars)
rg 'secret.*password|password.*jwt' -i
- Use gitleaks/trufflehog to scan for leaked secrets
- Find sensitive data in payloads: Audit JWT creation:
rg 'jwt\.encode|jwt\.sign' -A 5
- Check for PII:
ssn, password, credit_card, email, phone
- Detect missing expiration: Find tokens without exp claim:
rg 'jwt\.encode' -A 5 | rg -v 'exp|expir'
- Verify all tokens include expiration timestamps
Prevention
Related Security Patterns & Anti-Patterns
References