JSON Web Tokens (JWT) Security Best Practices
How JSON Web Tokens work under the hood, how to properly handle exp/iat expiration claims, and critical vulnerabilities to avoid in production.
Anatomy of a JWT: Three Base64Url Segments
A JSON Web Token (RFC 7519) is a compact, URL-safe representation of claims between two parties. It consists of three parts separated by dots (`.`): Header, Payload, and Signature.
The Header specifies the token type and cryptographic algorithm (e.g. HS256, RS256). The Payload contains the claims (user ID, roles, timestamps). The Signature is computed by hashing the header and payload with a secret or private key, preventing tampering.
The Crucial Standard Claims: exp, iat, and nbf
JWTs are stateless; once issued, the backend cannot easily revoke them without a blacklist or short expiry. Always enforce standard temporal claims:
`exp` (Expiration Time): The Unix timestamp after which the token is strictly invalid.
`iat` (Issued At): The Unix timestamp when the token was generated, useful for age calculation.
`nbf` (Not Before): The Unix timestamp before which the token must not be accepted.
The Infamous 'alg: none' Vulnerability
In early JWT library implementations, the specification allowed an algorithm value of `none` for unsecured tokens. Attackers could modify the payload, set `alg: 'none'`, strip the signature, and bypass authentication completely.
Modern verification libraries require explicitly whitelisting acceptable algorithms (e.g., only allowing `['HS256']` or `['RS256']`) to completely prevent downgrade attacks.
Secure Storage: HttpOnly Cookies vs LocalStorage
Storing access tokens in browser `localStorage` exposes them to any Cross-Site Scripting (XSS) vulnerability. If malicious JavaScript executes on your page, your user's tokens can be stolen immediately.
For web applications, the most secure approach is storing JWT session tokens in `HttpOnly, Secure, SameSite=Lax` cookies, preventing JavaScript access entirely while safeguarding against CSRF attacks.