Cryptography·May 12, 2026·6 min read

JWT alg Confusion: When RS256 Becomes HS256

A classic key-confusion bug lets an attacker sign tokens with the server's own public key. We walk through detection and the one-line server change that kills it.

#jwt#crypto#auth

JSON Web Tokens are only as safe as the verification logic behind them. When a server accepts whatever algorithm the token header declares, an attacker can downgrade an asymmetric RS256 token into a symmetric HS256 one — and sign it with the public key everyone already has.

Why it works

RS256 verifies with a public key. HS256 verifies with a shared secret. If the library uses the configured public key as the HMAC secret because the header says HS256, the attacker controls the signature.

json
{ "alg": "HS256", "typ": "JWT" } // forged payload signed with the RSA public key as the HMAC key

Detecting it safely

  • Confirm the token is normally RS256 / ES256.
  • On a test account, re-sign a benign token as HS256 using the public key.
  • If the server accepts it, the verifier is not pinning the algorithm.

The fix

Always pin the expected algorithm on the verifier and reject everything else. Never let the token's own header choose how it is validated.

ts
jwt.verify(token, publicKey, { algorithms: ['RS256'] });

root@offseccodes:~$ ./engage.sh

Want this tested on your own systems?

We run authorized assessments that find exactly these issues before attackers do.

Start an engagement