JWT Security Pitfalls and How to Avoid Them

Team 4 min read

#security

#jwt

#authentication

#webdev

#best-practices

Introduction

JSON Web Tokens (JWTs) are a popular tool for authenticating and authorizing users in modern web applications. They enable stateless authentication and scalable microservice architectures, but their security depends on careful design and handling. This post outlines common JWT pitfalls and practical strategies to avoid them.

Common Pitfalls

  • Treating JWTs as long-term session state
    • Using tokens with long lifetimes or no rotation can increase risk if a token is compromised.
  • Relying on symmetric signing with a secret exposed to the client
    • HS256/HS512 with a shared secret can be risky if the secret is leaked or shared across systems.
  • Not validating essential claims
    • Skipping checks for exp (expiration), iss (issuer), aud (audience), and iat/nbf can allow expired or misissued tokens to be accepted.
  • Allowing any algorithm or ignoring alg in the token header
    • Accepting algorithms like none or defaulting to an insecure algorithm can open downgrade attacks.
  • Storing tokens in non-secure storage on the client
    • LocalStorage or insecure cookies increase the risk of token theft via XSS.
  • Not distinguishing access tokens from refresh tokens
    • Reusing long-lived tokens for both access and refresh can widen the blast radius if compromised.
  • Including sensitive data in the JWT payload
    • JWT payload is not encrypted by default; put only non-sensitive, necessary claims in it.
  • Not rotating signing keys or using a mismatched key
    • Keys should be rotated and public keys should be retrieved via a trusted mechanism (e.g., JWKS).
  • Failing to validate the token’s audience and issuer
    • Without checking iss and aud, tokens issued for one system may be accepted by another.
  • Lack of token revocation or rotation strategy
    • Stateless tokens can’t easily be revoked; without a strategy, compromised tokens may remain valid until expiration.
  • Underestimating the risk of cross-origin usage
    • JWTs used across services/domains require careful scoping and audience design to avoid token leakage.

How to Avoid Them

  • Use short-lived access tokens and separate refresh tokens
    • Access tokens: short TTL (e.g., 5–15 minutes). Refresh tokens stored securely (HttpOnly cookies) with rotation.
  • Prefer asymmetric signing (RS256/ES256) and robust key management
    • Use a trusted key pair; expose only the public key and rotate keys via a JWKS endpoint.
  • Validate critical claims on every protected resource
    • Check exp, iss, aud, iat/nbf, and, if applicable, jti for replay prevention.
  • Whitelist allowed algorithms and reject others
    • Configure the verifier to only allow safe algorithms (e.g., [“RS256”,“ES256”]) and reject “none” or deprecated options.
  • Store tokens securely on the client
    • Use HttpOnly, Secure, SameSite cookies for tokens when feasible; avoid localStorage for sensitive tokens.
  • Distinguish access tokens from refresh tokens
    • Use different lifetimes and identifiers so a compromised refresh token doesn’t grant long-term access.
  • Avoid placing sensitive data in the JWT payload
    • Keep claims minimal and avoid PII or secrets in the token; store sensitive data server-side when needed.
  • Implement robust key rotation and JWKS usage
    • Fetch public keys from a JWKS endpoint and use the kid in the token header to select the correct key.
  • Enforce issuer and audience boundaries
    • Ensure the token’s iss matches your IdP and the aud matches your API or service scope.
  • Include a revocation strategy
    • Use short-lived tokens with a refresh flow, and consider a revocation list or one-time-use refresh tokens.
  • Consider token binding and additional protections
    • For higher security scenarios, pair JWTs with token binding, mutual TLS, or additional context checks on the server.

Implementation tips

  • Verification steps you should perform on every protected endpoint
    • Extract the token from the Authorization header (Bearer token).
    • Decode the header to read alg and kid, then fetch the correct public key (via JWKS if applicable).
    • Verify the signature using only allowed algorithms (e.g., RS256/ES256).
    • Validate claims: iss, aud, exp, iat/nbf, and optionally jti for revocation.
    • If valid, attach the user information from claims to the request context; otherwise, reject with 401.
  • Minimal example (conceptual)
  • Key management pattern
    • Publish a JWKS endpoint for public keys; rotate keys regularly and respond to key rollovers by updating cached keys.

Conclusion

JWTs are powerful when used correctly, but they come with security caveats that can be costly if ignored. By limiting token lifetimes, using strong signing methods and proper key management, validating essential claims, avoiding insecure storage, and implementing a solid revocation strategy, you can significantly reduce the risk associated with JWTs and build more secure authentication and authorization flows.