JWT alg:none & Algorithm Confusion
A JSON Web Token (JWT) is three base64url-encoded parts — header.payload.signature. The header declares which algorithm signs the token. The whole security model collapses when the verifier trusts that header instead of pinning the algorithm itself.
How it works
alg:none. The JWT spec defines an "unsecured" token with {"alg":"none"} and an empty signature. A naive verifier reads the alg field, sees none, and skips signature verification entirely. An attacker simply changes the payload (e.g. "role":"admin"), sets alg to none, and drops the signature:
header = base64url({"alg":"none","typ":"JWT"})
payload = base64url({"sub":"wiener","role":"admin"})
token = header + "." + payload + "." // note the trailing dot
Algorithm confusion (RS256 → HS256). When a server verifies asymmetric RS256 tokens with a public key, an attacker who knows that public key can sign a forged token with HS256 using the public key bytes as the HMAC secret. A verifier that picks the algorithm from the header will validate it as a legitimate HMAC.
How to test
Decode the token, flip a privilege claim, and try each variant: alg:none (and case variants like None, nOnE), then HS256 signed with the server's RSA public key. If /admin or an account endpoint accepts it, the verifier is vulnerable.
Impact
Full authentication bypass and privilege escalation — the attacker mints tokens for any user or role.
Defenses
- Pin the algorithm server-side; never derive it from the token header.
- Reject
alg:noneoutright. - Use separate, type-segregated keys for symmetric vs asymmetric verification.
- Prefer a vetted library configured with an explicit allow-list of algorithms.