JSON Web Tokens are convenient but easy to misuse. The usual root causes are: incorrect signature verification, trusting tokens without validating claims, weak secrets, poor token storage, missing revocation, and bad key management. Fixes are straightforward: use well-maintained libraries, always verify signatures and claims, prefer strong asymmetric keys with JWKs and key rotation, use httpOnly secure cookies or secure storage, implement refresh token rotation and revocation, and log suspicious behavior.
A practical explanation of the problem
JWTs are compact tokens that carry a header, claims, and a signature. Problems occur when developers treat them as opaque session IDs or forget that the signature and claims must be validated on every request. In production that leads to anything from trivial bypasses to full account takeover. Most “real-world” JWT compromises come from implementation mistakes — not the JWT spec itself.
Common real-world JWT mistakes and why they’re bad
1. Accepting alg: "none" or trusting the token alg header
Some libraries historically accepted tokens where the header said alg: "none", meaning “no signature”. If the server does not explicitly reject unsigned tokens, an attacker can present unsigned tokens that the server treats as valid.
Why fix: Always require a signature and enforce allowed algorithms server-side.
2. Algorithm confusion and key type mix-ups
Using HMAC (symmetric) secrets with code that expects RSA/EC (asymmetric) or vice versa can open up logic errors where an attacker can craft tokens that verify with a public key as an HMAC secret.
Why fix: Enforce expected algorithm family (HS256 vs RS256) and validate key types. Never accept an algorithm from an untrusted source.
3. Weak secrets and predictable keys
Short or guessable HMAC secrets can be brute forced offline.
Why fix: Use long, randomly generated secrets for HMAC or, better, use asymmetric keys (RSA/ECDSA) and protect private keys.
4. Missing or lax claim validation
Not validating iss (issuer), aud (audience), exp (expiry), nbf (not before), iat (issued at) allows replay and misuse across services.
Why fix: Always validate all relevant claims strictly and use short access token lifetimes.
5. Storing tokens insecurely
Storing JWTs in localStorage or exposing them in URLs invites theft via XSS, referrers, or logs.
Why fix: Use httpOnly secure cookies with SameSite where appropriate, avoid tokens in query strings, and defend against XSS.
6. No revocation or refresh token rotation
Treating JWTs as permanently valid until expiry means compromised tokens cannot be revoked. Long-lived tokens increase risk.
Why fix: Use short-lived access tokens + rotating refresh tokens with server-side revocation or token introspection.
7. Poor key management and no rotation
Static keys that never rotate increase blast radius when leaked.
Why fix: Use a JWK endpoint, rotate keys, and plan key rollovers and key IDs (kid) handling.
Defensive code and configuration patterns
Below are secure patterns to adopt. These are defensive examples to implement on servers, not attack commands.
Reject alg: none and enforce allowed algorithms
Node.js example using jsonwebtoken (verify with explicit options):
const jwt = require('jsonwebtoken');
const publicKey = fs.readFileSync('/etc/keys/jwt_pub.pem');
function verifyToken(token) {
// Only accept RS256 signed tokens, don't trust the token 'alg' header blindly
const opts = { algorithms: ['RS256'], issuer: 'https://auth.example.com', audience: 'api://default' };
return jwt.verify(token, publicKey, opts); // throws on invalid
}
Python PyJWT example:
import jwt
public_key = open('/etc/keys/jwt_pub.pem').read()
payload = jwt.decode(token, public_key, algorithms=['RS256'], issuer='https://auth.example.com', audience='api://default')
Validate critical claims every time
Prefer asymmetric keys with JWKs for distributed systems
Host a JWKs endpoint that publishes public keys. Use kid in header to lookup the right key and rotate keys without downtime.
Flow:
Secure cookie storage for browser-based apps
For SPAs consider:
Rotate and revoke refresh tokens
When a refresh token is used, issue a new one and mark the old one invalid. Keep minimal server-side state: a revocation list or token identifier in DB for refresh tokens.
Token introspection for opaque tokens
If you prefer statelessness but need revocation, use short-lived JWTs for access and opaque tokens for refresh or use a token introspection endpoint where resource servers can validate active sessions.
Example: secure refresh token rotation (conceptual)
This pattern mitigates replay of stolen refresh tokens.
Logging, monitoring, and alerts
Testing and CI checks
Realistic scenarios and lessons (brief)
Scenario A: A public key accidentally published as secret
A dev mistakenly stored the private RSA key in a public repo. Key rotated, but the team had no automated rotation process. Lesson: automation + short-lived keys reduce damage.
Scenario B: Access token leaked to third-party script via localStorage
A marketing widget loaded on pages had an XSS bug. Tokens in localStorage were stolen and used to call APIs. Lesson: do not store tokens in localStorage; prefer httpOnly cookies and harden against XSS.
Scenario C: Incomplete claim validation lets tokens issued for one service be accepted by another
Two microservices accepted tokens with different audiences. One accepted tokens for aud: service-a while the other accepted any aud. Attackers used a token from service A to call service B. Lesson: validate aud and iss per-resource.
Compact remediation checklist (copy into PR templates or runbooks)
Use well-maintained JWT libraries for your language.
Enforce signature verification and allowed algorithms server-side.
Prefer RS256/ES256 with protected private keys and JWKs published for verification.
Validate iss, aud, exp, nbf, iat, sub where applicable.
Keep access tokens short lived; implement rotating refresh tokens with server-side revocation.
Never store access tokens in localStorage; prefer httpOnly Secure SameSite cookies for browsers.
Do not pass tokens in query parameters.
Implement key rotation and monitoring for JWKs.
Log verification errors and set alerts for anomalous patterns.
Add CI tests that assert library behavior against known malformed tokens.
Further reading (standards and references)
© 2016 - 2025 Red Secure Tech Ltd. Registered in England and Wales under Company Number: 15581067