What is JWT?
JSON Web Token. A compact, URL-safe token format used for authentication and information exchange. Must be properly validated and stored securely.
Understanding JWT
JWTs contain three parts: header (algorithm), payload (claims/data), and signature. They're commonly used for stateless authentication but come with many security pitfalls. Common vulnerabilities include algorithm confusion attacks (changing alg to "none"), weak secrets that can be brute-forced, improper validation, and insecure storage.
Examples
- Using JWTs for API authentication
- Storing user roles and permissions in token claims
- Single sign-on (SSO) implementations
- Stateless session management
How to Prevent
- Always specify allowed algorithms explicitly in verification
- Use strong, random secrets (256+ bits) - generate with crypto.randomBytes()
- Set appropriate expiration times (exp claim) - typically 15min to 1 hour
- Don't store sensitive data in the payload - it's base64 encoded, NOT encrypted
- Store tokens in httpOnly cookies, not localStorage (prevents XSS theft)
- Implement token refresh mechanisms for long sessions
- Use asymmetric algorithms (RS256) when tokens are verified by multiple services
- Validate all claims: iss, aud, exp, nbf, iat
Code Examples
Algorithm Confusion Attack
The algorithm confusion attack tricks the server into using a different verification method than intended, allowing token forgery.
// VULNERABLE: Accepting any algorithm from the token
const jwt = require('jsonwebtoken');
const decoded = jwt.verify(token, publicKey);
// If attacker changes alg: "RS256" to alg: "HS256"
// The library might use the public key as an HMAC secret
// Attacker can forge tokens using the public key! // SECURE: Explicitly specify allowed algorithms
const jwt = require('jsonwebtoken');
const decoded = jwt.verify(token, publicKey, {
algorithms: ['RS256'] // Only accept RS256
});
// Now alg: "HS256" or alg: "none" will be rejected Weak Secret
// VULNERABLE: Weak, guessable secret
const token = jwt.sign(payload, 'secret123');
// Attacker can brute-force common secrets using tools like jwt_tool
// "secret", "password", "123456" are cracked instantly // SECURE: Strong, random secret (256+ bits)
const crypto = require('crypto');
const secret = crypto.randomBytes(64).toString('hex');
// Store this in environment variables, not code!
const token = jwt.sign(payload, process.env.JWT_SECRET);
// Or use asymmetric keys (RS256) for added security Secure Token Storage
// VULNERABLE: Storing JWT in localStorage
localStorage.setItem('token', jwt);
// XSS can steal this: localStorage.getItem('token')
// Any script on the page can access it // SECURE: HttpOnly cookie (server-side)
res.cookie('token', jwt, {
httpOnly: true, // JavaScript cannot access
secure: true, // HTTPS only
sameSite: 'strict', // CSRF protection
maxAge: 3600000 // 1 hour expiry
});
// For SPAs: Consider token in memory + refresh token in httpOnly cookie Real-World Incidents
Auth0 Critical Vulnerability
2015Algorithm confusion vulnerability in Auth0's JWT library allowed attackers to forge tokens by switching from RS256 to HS256.
Impact: All applications using the vulnerable library were at risk of complete auth bypass
Plex Data Breach
2022Attackers accessed a database containing user data. Passwords were hashed, but JWT secrets were also exposed, potentially allowing session hijacking.
Impact: 20+ million users notified to change passwords
Worried about JWT in your app?
Our security audits identify vulnerabilities like this before attackers do. Get expert manual review of your codebase.