← Wiki

JWT alg:none & Algorithm Confusion

critical · 7 min read · updated 2026-06-01

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:none outright.
  • Use separate, type-segregated keys for symmetric vs asymmetric verification.
  • Prefer a vetted library configured with an explicit allow-list of algorithms.