JWT Algorithm Confusion Attack — How Attackers Bypass Signature Verification
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
- Server is configured to use RS256 (asymmetric) and exposes a public key
- Attacker creates a token with
alg: HS256(symmetric) - Attacker signs the token using the server's PUBLIC key as the HMAC secret
- Server receives the token, reads
alg: HS256, and uses the public key (which it has) as the HMAC secret - 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
- How does the algorithm confusion attack work?
- What is the primary defense against this attack?
- Why does the attacker use the public key as an HMAC secret?
- Can this attack work if the server only supports ES256?
- How do you test if your JWT verification is vulnerable?
Answers:
- 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.
- Always specify an explicit algorithm allowlist when decoding (
algorithms=["RS256"]). Never trust thealgheader. - 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.
- In theory, yes, if the library is confused. In practice, ES256 key formats are distinct enough that this is harder but still possible.
- Create a token with
alg: HS256signed 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
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