JWT: Structure and Validation
A JSON Web Token (JWT) is a base64url-encoded JSON structure used to represent claims between two parties. It is self-contained: the server does not need a session store to validate it. All information needed for validation is in the token itself, including the algorithm used to sign it. That design decision is the source of most JWT vulnerabilities.
Structure
A JWT consists of three base64url-encoded sections separated by dots:
header.payload.signature
The header is a JSON object specifying the token type and signing algorithm:
{"alg": "HS256", "typ": "JWT"}
The payload contains claims: statements about the subject and additional metadata. Registered claim names defined by RFC 7519 include sub (subject), iss (issuer), exp (expiration time), iat (issued at), and aud (audience). Applications add their own claims alongside these.
The signature is computed over the concatenated base64url-encoded header and payload:
HMACSHA256(base64url(header) + "." + base64url(payload), secret)
For asymmetric algorithms such as RS256, the signature is computed with the private key and verified with the public key.
What Correct Validation Looks Like
A library validating a JWT must perform all of the following steps, in this order:
- Parse the header and extract the
algfield. - Verify that the algorithm in the header matches an algorithm the application has configured as acceptable. If the application expects RS256, tokens claiming HS256 or
nonemust be rejected before any signature verification attempt. - Retrieve the appropriate key for the algorithm and verify the signature over the header and payload. If signature verification fails, reject the token.
- Verify the
expclaim is in the future. - Verify the
issclaim matches the expected issuer. - Verify the
audclaim matches the expected audience. - Trust the payload claims only after all of the above pass.
Step 2 is where most historical JWT vulnerabilities originate. Early library implementations read the algorithm from the header and passed it directly to the verification function without checking whether it was acceptable. The token itself was trusted to declare how it should be verified.
Stateless Verification and Its Costs
Because the server does not store sessions, a valid JWT cannot be individually revoked before its expiration time. If a token is stolen or a user's access should be terminated immediately, the server has no mechanism to invalidate that specific token. Revocation requires either a token blocklist (which reintroduces server-side state) or very short expiration windows combined with refresh tokens.
Short-lived access tokens with longer-lived refresh tokens is the standard pattern. The access token expires in minutes; the refresh token, stored more carefully, is used to obtain new access tokens. Revoking the refresh token prevents further access token issuance, bounding the window of misuse to the remaining lifetime of the last issued access token.