
JSON Web Token vulnerabilities have been documented since 2015. Most major libraries addressed them years ago. They continue to appear in assessments, because the flaws do not live in the libraries any more — they live in the code wrapped around them.
The none algorithm
The specification permits alg: none for unsecured tokens. A verifier that honours the header’s algorithm claim will accept a token with no signature at all.
Modern libraries reject this by default. It reappears when a developer writes a permissive wrapper, or passes a list of allowed algorithms built from the token rather than from configuration, or handles an edge case by falling through to unverified decoding. Always test it; it costs thirty seconds and occasionally still works.
Algorithm confusion: RS256 to HS256
The more interesting one. The server issues tokens signed with RS256 — asymmetric, private key signs, public key verifies. The attacker changes the header to HS256 — symmetric, one key both signs and verifies — and signs the token using the server’s public key as the HMAC secret.
A verifier that selects its algorithm from the token header will use HS256, reach for the configured key (the public key, since that is what it verifies with), and validate the signature successfully. The public key is public by definition, so the attacker can mint arbitrary tokens.
The fix is to pin the algorithm in the verification call rather than reading it from the token. The token is attacker-controlled; the header is not a source of truth about how to verify it.
Key confusion through kid, jku and jwk
These header parameters tell the verifier which key to use, which makes them an injection surface.
kidused as a filesystem path — path traversal to a file with predictable contents, then sign with that.kidconcatenated into a SQL query — injection returning an attacker-chosen key.jkupointing at a URL the verifier fetches — host a key set, sign with the matching private key. Also an SSRF primitive.jwkembedding the key directly in the token — a verifier that trusts it accepts anything.
Key selection should come from server-side configuration keyed by a value the server controls, and jku and jwk should be ignored unless there is a specific reason to support them and a strict allowlist.
Weak secrets on HS256
Where HS256 is used legitimately, the secret is often a short string committed to a repository. These are crackable offline from a single captured token, and word lists for common framework defaults exist. We test every HS256 token against them.
Claim validation
Signature verification only establishes that the token was issued by someone holding the key. The claims still need checking:
exp— verified, with a bounded clock skew tolerance.aud— a token minted for one service accepted by another is a real finding in microservice estates.iss— particularly where multiple identity providers are configured.- Authorisation claims — roles and scopes inside the token are only as trustworthy as the signature, and a token that cannot be revoked before
expis a design decision worth making deliberately.
Revocation
The structural property of JWTs is that they are self-contained, so a valid token remains valid until it expires. Where a session must be terminable — a password change, an account suspension, a lost device — something server-side has to track that, which reintroduces the lookup JWTs were adopted to avoid. Long-lived tokens without a revocation path are one of the most common design findings we report.