SecureBlockLog inStart a pentest
Vulnerability Repository
CriticalAuthentication

JWT Algorithm Confusion

Flawed JWT validation that trusts the algorithm field allows attackers to forge tokens using the public key as an HMAC secret or by setting algorithm to 'none'.

CVSS 9.1CWE CWE-327OWASP A02:2021 — Cryptographic Failures

Description

JWT (JSON Web Token) algorithm confusion attacks exploit vulnerabilities in how servers validate the cryptographic signatures on JWT tokens. JWTs consist of three base64url-encoded parts: a header (containing the algorithm), a payload (containing claims), and a signature. The critical vulnerability arises when the server trusts the alg field in the JWT header provided by the client rather than enforcing a fixed expected algorithm server-side.

The two primary attack variants are the none algorithm attack and the RS256 to HS256 algorithm confusion attack. Both fall under CWE-327 (Use of a Broken or Risky Cryptographic Algorithm) and OWASP A02:2021 (Cryptographic Failures), and both allow an attacker to forge arbitrary JWT payloads — including escalated privilege claims — and have them accepted as valid by the server.

Algorithm confusion vulnerabilities appear across virtually every technology stack because JWT libraries historically defaulted to trusting the client-supplied alg header. Even today, misconfigurations in JWT validation logic are a first-class finding in API penetration testing engagements, particularly in microservice architectures where JWTs are used for service-to-service authentication.

How It Works

Attack 1: none algorithm — the JWT spec originally allowed an alg of none to represent an unsigned token. A vulnerable library accepts this:

  1. Take a valid JWT: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJyb2xlIjoidXNlciJ9.SIGNATURE
  2. Decode the header and payload, modify the payload to {"user":"alice","role":"admin"}
  3. Re-encode with alg: none and an empty signature:
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyIjoiYWxpY2UiLCJyb2xlIjoiYWRtaW4ifQ.
  1. A vulnerable library accepts this token and grants admin privileges.

Attack 2: RS256 to HS256 confusion — the more sophisticated and widely applicable attack:

The server uses RS256 (RSA asymmetric signing): private key signs, public key verifies. The server's public key is often available at /.well-known/jwks.json or /api/v1/public-key.

The attacker switches the algorithm to HS256 (HMAC symmetric signing) and signs the forged token using the server's public key as the HMAC secret:

import jwt
import requests

# Fetch the server's public key
public_key = requests.get('https://target.com/.well-known/jwks.json').text

# Forge a token with elevated privileges, signed with the public key as HMAC secret
forged_token = jwt.encode(
    {"user": "alice", "role": "admin", "exp": 9999999999},
    public_key,
    algorithm="HS256"
)

When the vulnerable server receives this token, it reads alg: HS256 from the header, looks up its public key (which it uses for RS256 verification but also stores in memory), and uses that key as the HMAC secret — exactly matching the attacker's forged signature. Authentication succeeds.

Burp Suite's JWT Editor extension includes a dedicated "Algorithm Confusion" attack module that automates both attacks, fetching the public key and generating forged tokens in one click.

Impact

  • Complete Authentication Bypass — Forging tokens with arbitrary user IDs and roles to impersonate any user including administrators
  • Privilege Escalation — Modifying the role, scope, permissions, or isAdmin claims in the token payload
  • Cross-Tenant Access — Changing the tenant_id or org_id claim to access another customer's data
  • Full API Authorization Bypass — In microservice architectures, forging inter-service JWTs to call any internal API endpoint

Detection

  1. Test none algorithm — decode a valid JWT, modify the payload, re-encode the header with alg: none, remove the signature, and submit. Use Burp Suite JWT Editor's "Alg: None" attack.
  2. Fetch the server's public key — check /.well-known/jwks.json, /api/jwt/public-key, /oauth/.well-known/openid-configuration. If available, proceed to the algorithm confusion test.
  3. Test RS256 to HS256 confusion — use Burp Suite JWT Editor's "Algorithm Confusion" button to automatically perform the attack with the fetched public key.
  4. Test kid (Key ID) injection — check whether the kid header parameter is used in a SQL query or file path to load the key: {"kid": "../../dev/null"} with an empty signature, or {"kid": "' UNION SELECT 'attacker_key'--"}.
  5. Check jwk header injection — some libraries accept a jwk parameter in the JWT header specifying the verification key. Embed your own public key in the header and sign with the corresponding private key.
  6. Verify the library version — check the JWT library version against known CVEs. jsonwebtoken < 9.0, PyJWT < 2.4, and java-jwt < 4.0 had algorithm confusion vulnerabilities.

Remediation

Hardcode the expected algorithm server-side. Never trust the alg field from the token. Specify the expected algorithm explicitly during verification:

// Node.js — jsonwebtoken (secure)
const decoded = jwt.verify(token, publicKey, { algorithms: ['RS256'] });

// Never do this:
const decoded = jwt.verify(token, publicKey); // trusts alg from header
# Python — PyJWT (secure)
decoded = jwt.decode(token, public_key, algorithms=["RS256"])

Reject none algorithm explicitly. Ensure the none algorithm is not in the list of accepted algorithms under any circumstances.

Use separate key pairs per algorithm. If you support both RS256 and HS256, use different keys. This prevents a public RSA key from being usable as an HMAC secret.

Keep JWT libraries up to date. Subscribe to security advisories for your JWT library. Algorithm confusion patches are frequently released as the spec evolves.

Validate all standard claims. After signature verification, always validate exp (expiry), iss (issuer), and aud (audience) to limit the utility of any forged tokens.

Ready when you are
Scope a pentest in the next two minutes.
Start scoping