JWT Security Pitfalls and How to Avoid Them
JWTs are easy to issue and easy to get wrong. Most JWT vulnerabilities come from how tokens are verified, not from the format itself.
1. Always verify, and pin the algorithm
import jwt from 'jsonwebtoken';
// Wrong: decode() does NOT check the signature
const payload = jwt.decode(token);
// Right: verify the signature and allow only the algorithm you use
const payload = jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256'],
issuer: 'https://auth.example.com',
audience: 'shop-api',
});
Without an explicit algorithm list, attacks like alg: none or "algorithm confusion" (an RS256 public key reused as an HS256 secret) become possible with some libraries.
2. Use strong keys
- HS256 secrets must be long and random (32+ bytes), not "secret" or a word. Weak secrets can be brute-forced offline from a single token.
- For multiple services, prefer asymmetric keys (RS256, ES256, EdDSA): only the auth server can sign, everyone can verify with the public key.
- Support key rotation with a
kidheader and a JWKS endpoint.
3. Check the claims
exp- reject expired tokens (libraries do this by default; don't turn it off).issandaud- a token meant for another service must not be accepted by yours.nbfandiat- sanity-check time-based claims.
4. Don't put secrets in the payload
echo 'eyJzdWIiOiI0MiIsInJvbGUiOiJhZG1pbiJ9' | base64 -d
# {"sub":"42","role":"admin"} <- anyone can read it
5. Keep them short-lived
JWTs can't easily be revoked. Expiry is your main control: minutes for access tokens, with refresh token rotation for longer sessions.
6. Store them carefully in browsers
localStorage is readable by any script on the page, so one XSS bug leaks every token. Prefer HttpOnly, Secure cookies (with CSRF protections), or keep access tokens in memory only.
7. Re-check permissions that change
A role: admin claim stays in the token until it expires, even after you demote the user. For sensitive actions, check current permissions from the database.
Comments
No comments yet. Have a question or a tip? Be the first to share it.