Skip to content
Back to Blog
Engineering

JWT Algorithm Confusion Explained: Forging Tokens with RS256 to HS256

How JWT algorithm confusion works, why switching RS256 to HS256 lets an attacker sign their own tokens with a public key, and how to shut it down in your own code.

September 5, 2026 by javelin

JSON Web Tokens are everywhere in modern authentication, and algorithm confusion is one of the few JWT bugs that turns a read-only foothold into full account takeover. The attack is simple once you see it: the token tells the server how to verify the token. If the server believes it, an attacker can pick an algorithm the server never intended to accept.

What is JWT algorithm confusion?

Algorithm confusion is an attack where a signed JSON Web Token is modified so its alg header names a different signing algorithm than the server intended, and the server verifies it anyway using the wrong key. In the common case an attacker changes RS256 to HS256, then signs the forged token with the server’s own public key. Because the server passes that same public key into an HMAC verification routine, the signature checks out and the attacker can set any claim they like, including an administrator role. The bug is not in the cryptography. It is in letting untrusted input choose which verification path runs.

Why RS256 and HS256 make this possible

The two algorithms have fundamentally different key models, and that mismatch is the whole bug.

RS256HS256
TypeAsymmetricSymmetric
Signs withPrivate keyShared secret
Verifies withPublic keyThe same shared secret
Key is secret?Only the private keyYes, always

Under RS256 the public key is meant to be public. Publishing it is the entire point, since anyone should be able to verify a token without being able to mint one. Under HS256 the verification key and the signing key are the same value, so anything that can verify can also sign.

Now consider a library call like verify(token, key) where key holds the RSA public key and the algorithm is read from the token itself. Send a token with "alg": "HS256" and the library treats that public key as an HMAC secret. The attacker knows the public key. So the attacker can produce a valid signature.

!

The dangerous ingredient is trusting the alg header. A verifier that accepts whatever algorithm the token names is deciding security policy based on attacker-controlled input.

The attack, step by step

1. Obtain the public key

Sometimes the key is simply published, at /jwks.json, /.well-known/jwks.json, or in API documentation. When it is not exposed directly, it can often be recovered from two tokens signed by the same private key, which is enough to solve for the RSA modulus.

2. Change the algorithm and the claims

Decode the token, set the header to {"alg": "HS256", "typ": "JWT"}, and edit the payload to claim whatever the application checks, typically something like "role": "admin" or "isAdmin": true.

3. Sign with the public key as the HMAC secret

This is the step that trips people up. The HMAC secret must be the exact byte sequence the server passes to its verify call, usually the PEM-encoded public key including its header and footer lines and its trailing newline. A single byte of difference produces a different signature and the forgery fails, which is why this attack is often abandoned one step before it would

Related team notes, research commentary and lessons learned.