JSON Web Tokens: How They Work and Security Pitfalls
How JWTs work for authentication and authorization, the difference between signed and encrypted tokens, and critical implementation mistakes to avoid.
Continue your mission
How JWTs work for authentication and authorization, the difference between signed and encrypted tokens, and critical implementation mistakes to avoid.
A JSON Web Token (JWT, pronounced "jot") is a compact, URL-safe token format used to transmit claims between two parties. JWTs are widely used for authentication and authorization in web applications and APIs. Unlike traditional session IDs, JWTs are self-contained: they carry all the information needed to verify the user's identity and permissions without requiring a server-side session lookup.
A JWT consists of three Base64URL-encoded parts separated by dots: header.payload.signature
The header specifies the token type and signing algorithm: {"alg": "HS256", "typ": "JWT"}
The payload contains claims, which are key-value pairs of information. Standard claims include sub (subject/user ID), iat (issued at), exp (expiration), and iss (issuer). Custom claims can include roles, permissions, or any other data.
The signature ensures the token has not been tampered with. For HMAC-based algorithms, the signature is created by hashing the header and payload with a secret key. For RSA or ECDSA, the signature uses a private key and can be verified with the corresponding public key.
A typical JWT authentication flow works as follows: The user submits credentials. The server verifies them and generates a JWT containing the user's ID and roles. The server returns the JWT to the client. The client stores the JWT (usually in memory or an HttpOnly cookie) and includes it in the Authorization header of subsequent requests. The server validates the JWT's signature and expiration on each request, then extracts the user's identity and permissions from the payload.
Most JWTs are signed (JWS) but not encrypted. This means anyone can read the payload by Base64-decoding it. The signature only prevents modification, not observation. Never put sensitive information (passwords, social security numbers, API keys) in a JWT payload.
Encrypted JWTs (JWE) encrypt the entire payload so it cannot be read without the decryption key. JWE is less common and adds complexity. If you need to transmit sensitive data, consider whether JWE is necessary or if the data should simply remain server-side.
The "alg: none" attack. Some JWT libraries accept tokens where the algorithm is set to "none," meaning no signature is required. An attacker can forge a token with any claims and the server will accept it. Always validate that the algorithm in the token header matches what your server expects, and reject "none."
HMAC/RSA confusion. If a server expects RSA-signed tokens (verified with a public key) but an attacker sends an HMAC-signed token using the server's public key as the HMAC secret, some libraries will accept it. This happens because the verification code sees "HS256" and uses the configured key (the public RSA key) as an HMAC secret. The fix: explicitly specify the expected algorithm during verification, never let the token dictate it.
Not validating expiration. Always check the exp claim. Without expiration validation, stolen tokens are valid forever.
Storing JWTs in localStorage. This makes them accessible to any JavaScript running on the page, including XSS payloads. Prefer HttpOnly cookies for storage, or keep tokens in memory and use refresh token rotation for persistence.
Overly long-lived tokens. JWTs cannot be revoked server-side (there is no session to invalidate) unless you maintain a token blocklist, which negates the stateless benefit. Keep access token lifetimes short (5 to 15 minutes) and use refresh tokens for longer sessions.
JWTs work well for stateless API authentication, especially in microservice architectures where passing a self-contained token avoids a database lookup on every request. They are also useful for single sign-on (SSO) implementations.
For traditional web applications with a single backend, server-side sessions with session IDs are often simpler and more secure. The ability to immediately invalidate a session by deleting it from the server is a significant advantage over JWTs.
CDA Theater missions that address topics covered in this article.
Cryptographic technique that encrypts data while preserving its original format and length, enabling protection without breaking legacy system compatibility.
Guide to HTTP/2 security covering binary framing, HPACK compression attacks, rapid reset vulnerability, stream multiplexing risks, and mitigation strategies.
Explanation of Certificate Transparency framework, covering log servers, Signed Certificate Timestamps, monitoring capabilities, and detection of fraudulent certificates.
Written by CDA Wiki Team
Found an issue? Help improve this article.