← All Articles
Security7 min read

JWT Security: 8 Common Mistakes That Get Applications Breached

January 18, 20257 min read

JWTs are deceptively simple. Three base64-encoded sections, a signature, done. But the implementation details matter enormously — mistakes here have led to authentication bypasses in production applications.

1. Using HS256 with weak secrets

HMAC-SHA256 (HS256) is only as strong as your secret key. A 16-character secret can be brute-forced offline given a valid token. Use a secret of at least 256 bits (32 random bytes) generated by a cryptographically secure random number generator. Or better: switch to RS256/ES256 with asymmetric keys — the private key signs, the public key verifies.

2. Storing JWTs in localStorage

localStorage is accessible to any JavaScript on the page. A single XSS vulnerability on your domain gives an attacker your users' tokens. Store access tokens in memory (a JavaScript variable) and refresh tokens in httpOnly, SameSite=Strict cookies. httpOnly cookies are inaccessible to JavaScript by design.

3. Long expiry times

A JWT with a 30-day expiry that gets stolen is valid for 30 days. Use 15-minute access tokens with refresh token rotation. When a refresh token is used, immediately invalidate it and issue a new one. Detect refresh token reuse — if a rotated token is presented, it indicates a token theft, and all sessions for that user should be invalidated.

4. Not validating the algorithm

The classic "alg:none" attack. Some JWT libraries historically accepted tokens with the algorithm set to "none" — no signature required. Always explicitly specify the expected algorithm when verifying. Never accept the algorithm from the token header itself without validation.

5. Ignoring token revocation

JWTs are stateless by design, which makes revocation hard. But "hard" doesn't mean "skip it." For sensitive operations (password change, account deletion, suspicious activity), maintain a short-lived revocation list in Redis. Check the list on every request. The performance overhead is negligible.

6. Missing audience and issuer validation

Validate the aud (audience) and iss (issuer) claims on every token. A token issued by your staging environment should not be valid in production. A token issued for your mobile app should not grant access to your admin API.

7. Sensitive data in the payload

JWT payloads are base64-encoded, not encrypted. Anyone with the token can decode the payload. Never include passwords, credit card numbers, SSNs, or other sensitive data in a JWT payload. Include only the minimum necessary claims: user ID, roles, expiry.

8. No key rotation

If your signing key is compromised, all issued tokens are compromised. Implement key rotation: publish a JWKS (JSON Web Key Set) endpoint with multiple active keys. Sign new tokens with the current key. Accept tokens signed with any active key. Retire old keys after their longest-lived tokens expire.

GET STARTED

Ready to build
something exceptional?

From idea to launch in weeks, not months. Let's talk about your project.