JWT algorithm confusion: alg: none, HS256-vs-RS256, and how to defend

Algorithm confusion attacks exploit a verifier that trusts the header's alg field. Pin the algorithm at the verifier and reject anything else.

shield

Private: Decoding, analysis, signature verification, and token generation all run in your browser. Nothing is sent to any server, except a JWKS URL you explicitly enter and click Verify on.

At a glance

Vulnerability
Trusting header.alg
alg: none attack
Forge token, set alg=none, no signature
HS256/RS256 confusion
Use public key as HMAC secret
Defense
Pin algorithms list at verifier

Two classic JWT vulnerabilities exploit verifiers that trust the header's `alg` field too much. The "alg: none" attack accepts unsigned tokens. The HS256/RS256 confusion attack tricks a verifier configured for RS256 into validating an HS256 token using the RSA public key as the HMAC secret. Both are decisively defeated by pinning the algorithm at the verifier.

Pin the algorithm — the only real defense

Every reputable JWT library now lets you pass an `algorithms` allowlist to the verifier. The library rejects any token whose header `alg` is not in that list before attempting verification. This makes algorithm confusion attacks structurally impossible — the verifier never reaches the part where it picks an algorithm based on attacker input.

In `jose`: `jwtVerify(token, key, { algorithms: ["RS256"] })`. In `jsonwebtoken`: `jwt.verify(token, key, { algorithms: ["RS256"] })`. Always pass it. There is no scenario in production where you legitimately need to accept any algorithm.

Frequently asked questions

Why was alg: none ever valid?expand_more
RFC 7519 allows it for use cases where the integrity of the token is provided externally (e.g., the channel is already authenticated). Early libraries treated it like any other algorithm, so a verifier configured to "accept any signed token" would accept unsigned ones too. Modern libraries reject `none` unless you opt in explicitly, but legacy code remains in production.
How does the HS256/RS256 confusion attack work?expand_more
The verifier expects RS256 (asymmetric) and is configured with a public key. The attacker forges a token, sets `alg: HS256` in the header, and signs the body using the *public key* as the HMAC secret. If the verifier picks the algorithm from the header, it computes HMAC-SHA256 with the public key, gets a match, and accepts the forged token.
How do I defend?expand_more
Configure the verifier with the exact list of allowed algorithms (`algorithms: ["RS256"]`) and let the library reject any token whose header doesn't match. Don't use APIs that "auto-detect" the algorithm from the header. The analyzer flags `alg: none` tokens as a Critical finding.

Related guides

Related Tools