JSON Web Tokens (JWT) (RFC 7519) have become the ubiquitous standard for stateless authentication in modern distributed web architectures and microservices. When a user logs in, the authentication server issues a cryptographically signed compact string containing user claims. However, widespread developer misunderstandings regarding token signing versus encryption have led to severe enterprise account takeover vulnerabilities.
A JWT consists of three distinct components separated by periods: Header.Payload.Signature
Figure 29.1: Anatomy of a JSON Web Token and Critical Exploitation Vectors
A standard JWT uses JWS (JSON Web Signature). The Header and Payload are simply Base64URL-encoded JSON. Anyone who intercepts the token can decode and read all contents in cleartext! Never place sensitive secrets, credit card numbers, or passwords inside a JWT payload.
The JWT specification permits unsecured tokens with "alg": "none" (used historically for testing). In vulnerable libraries, if an attacker changes the header algorithm to "none" and removes the signature portion (keeping the trailing period Header.Payload.), the server validates the token without verifying any signature at all, granting instant admin access!
In an asymmetric setup (RS256), the authorization server signs tokens with its private key, and backend microservices verify tokens using the public key. In an algorithm confusion attack:
/.well-known/jwks.json).{"role": "admin"}.RS256 to HS256 (symmetric HMAC).alg, it runs HMAC-SHA256 using its public key—and the signature matches!| Storage Location | Vulnerability to XSS | Vulnerability to CSRF | Recommended Architecture |
|---|---|---|---|
localStorage / sessionStorage |
100% VULNERABLE! Any XSS bug executes localStorage.getItem('token') and steals the session. |
Immune to CSRF. | NOT RECOMMENDED for sensitive session tokens. |
HttpOnly; Secure; SameSite=Strict Cookie |
100% IMMUNE to XSS token theft! JavaScript cannot read the cookie. | Protected by SameSite=Strict and anti-CSRF headers. |
GOLD STANDARD! Protects user sessions from client-side script theft. |
const jwt = require('jsonwebtoken');
function verifyToken(token, publicKey) {
// STRICTLY PIN the expected algorithm to defeat algorithm confusion!
return jwt.verify(token, publicKey, {
algorithms: ['RS256'], // Whitelist ONLY RS256! Rejects 'none' and 'HS256'
issuer: 'https://auth.veewom.com',
maxAge: '15m' // Enforce short token lifespan
});
}
Broaden your defensive and offensive security knowledge across our curriculum:
Where JWT flaws fit into modern API authentication and session architectures.
Why storing JWTs in cookies with HttpOnly is superior to localStorage.
How cryptographic hash functions guarantee JWT signature immutability.
Writing safe JWT verification code with explicit algorithm pinning in Node.js.