Algorithm Confusion and the none Attack
The alg field in a JWT header is client-controlled. An attacker can set it to none or confuse the signing type. Pin the algorithm server-side.
The JWT header has an alg field. This field is client-controlled. If the server trusts it, the attacker controls the verification path.
The none attack
The attacker sets the header to {"alg":"none"} and deletes the signature. The token becomes two parts with an empty third part:
Normal token:
eyJhbGci... . eyJzdWIi... . SflKxwRJ...
header payload signature
none attack token:
eyJhbGci... . eyJzdWIi... .
{"alg":"none"} {"sub":"admin"} ← empty
The server reads alg: "none" and skips verification. The empty signature is not a problem. The server expects none. The attacker can put any payload they want. Case variants (nOnE, NONE) bypass naive string checks.
Algorithm confusion
The server uses RS256 (asymmetric). The attacker switches alg to HS256 and signs with the exposed public key. The generic verify() function treats the public key as an HMAC secret.
RS256 setup:
private key (secret) → signs
public key (public) → verifies
Attack: switch alg to HS256
HMAC needs ONE key for sign + verify
attacker uses public key as that key
server also has public key
→ HMAC(publicKey, token) matches both sides ✗
This attack works only when the server has an RSA key pair and a generic verify() that accepts both HS256 and RS256.
Warning:
jwt.verify(token, secret)with noalgorithmsoption trusts the token header. One option field blocks both attacks.
From the audit: lobby-service and game-server called
jwt.verify(token, secret)without an algorithm pin. After the fix, both passalgorithms: ['HS256']. The project only uses HS256, so the confusion path does not apply. But the pin blocks the none attack.
“The
algfield is client-controlled. One option takes control back to the server.”
References:
Related: back to the Seven Blind Spots Behind a JWT Audit overview · Symmetric vs Asymmetric Signing · Six Standard Claims