Skip to content
All writing Part 05 of 07 · Seven Blind Spots Behind a JWT Audit
Engineering · 3 min read

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.

Token header: alg = ? "none" No signature needed ✗ "HS256" (server expects RS256) Signs with public key as HMAC secret ✗ Server accepts forged token FIX: pin algorithm server-side jwt.verify(t, key, {algorithms:['HS256']}) → ignores token header ✓

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 no algorithms option 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 pass algorithms: ['HS256']. The project only uses HS256, so the confusion path does not apply. But the pin blocks the none attack.

“The alg field 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

Tags #jwt #security #authentication
// connect

Be brave | Be wise | Be grateful

21 BreakinCode

// elsewhere
LinkedInMedium (lang: en)Life RecordYoutube
wh:~$William Hung· © 2026 Taipei · GMT+8 · Available for collaboration