JWT Security Pitfalls Every Developer Should Know

Published jwtsecurityauthenticationwebdev

JWT adoption exploded because they’re stateless and elegant. They also come with a set of security footguns that have bitten real production systems. The alg:none vulnerability bypassed authentication in multiple widely-used libraries. Storing PII in payloads has leaked user data. Missing exp validation has left tokens valid forever. This guide covers the five pitfalls that actually get applications breached.

Pitfall 1 — The alg:none Attack

Some early JWT libraries accepted "alg": "none" in the header as a valid algorithm, meaning no signature was required. An attacker could:

  1. Decode any JWT (the payload is just base64)
  2. Modify the payload (change role: "user" to role: "admin")
  3. Re-encode with "alg": "none" and an empty signature
  4. The vulnerable library would accept it as valid
// ❌ Vulnerable: trusting the token's own alg header
const decoded = jwt.verify(token, secret, { algorithms: [decoded_header.alg] });

// ✅ Fix: always specify allowed algorithms explicitly
const decoded = jwt.verify(token, secret, { algorithms: ['HS256'] });

Always pass an explicit algorithms allowlist to jwt.verify(). Never derive the algorithm from the token itself.

Pitfall 2 — Weak or Hardcoded Secrets

HS256 JWTs signed with a weak secret (e.g. "secret", "password", the app name) can be brute-forced offline. An attacker only needs one valid token.

// ❌ Weak secrets
const secret = 'secret';
const secret = 'myapp';

// ✅ Use a cryptographically random secret of at least 256 bits
const secret = crypto.randomBytes(32).toString('hex'); // generate once, store in env

For asymmetric algorithms (RS256, ES256), use key pairs — the private key signs, the public key verifies. The public key can be shared safely.

Pitfall 3 — Sensitive Data in Payloads

JWTs are signed, not encrypted. Anyone who intercepts the token can decode the payload in one line. This gets developers because the token “looks” like garbled text.

// Anyone can run this on your token:
JSON.parse(atob(token.split('.')[1]));
// → { userId: 1, email: "user@example.com", role: "admin", creditCard: "4111..." }

Rule: put only non-sensitive identifiers in the payload (userId, role, sessionId). Never put passwords, PII beyond what’s essential, payment data, or secrets.

If you need encrypted payloads, use JWE (JSON Web Encryption) — a different spec to JWT.

Pitfall 4 — Missing or Ignored exp Validation

If you decode a JWT without verifying the signature AND without checking exp, stolen tokens are valid forever.

// ❌ Decoding without verification — fine for debugging, catastrophic in prod
const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64url'));

// ✅ Always verify — jsonwebtoken checks exp automatically
const payload = jwt.verify(token, secret); // throws TokenExpiredError if exp is past

Also: set reasonable token lifetimes. Access tokens: 15 minutes to 1 hour. Refresh tokens: days to weeks with rotation.

Pitfall 5 — Storing JWTs in localStorage

localStorage is accessible to any JavaScript on the page, including injected scripts from XSS attacks. A single XSS vulnerability can exfiltrate every stored token.

StorageXSS riskCSRF riskVerdict
localStorageHighLow❌ Avoid for auth tokens
sessionStorageHighLow❌ Same as localStorage
httpOnly cookieNoneMedium✅ Preferred for access tokens
Memory (JS variable)LowLow✅ For short-lived tokens

Use httpOnly; Secure; SameSite=Strict cookies for auth tokens. Add CSRF protection if needed.


Before deploying, use our JWT token decoder to audit what data you’re actually putting in your token payloads — it’s easy to accidentally include more than intended.

Further Reading

Try it in your browser

No setup needed — use our free JWT Decoder directly online.

Open JWT Decoder →

Sven Schuchardt

Management Consulting · Enterprise Architecture

Bridging the gap between business need and IT & Architecture enablers. With a background in management consulting and enterprise architecture, translating complex technology decisions into clear, actionable insights — written for every stakeholder, from the boardroom to the engineering team.

Connect on LinkedIn