Security
12 min read
324 views

JWT Algorithm Confusion: Turning RS256 Tokens into HS256 Disasters 🔄

IT
InstaTunnel Team
Published by our engineering team
JWT Algorithm Confusion: Turning RS256 Tokens into HS256 Disasters 🔄

JWT Algorithm Confusion: Turning RS256 Tokens into HS256 Disasters 🔄

Understanding the Critical Security Flaw That Turns Public Keys into Secrets

In the landscape of modern web application security, JSON Web Tokens (JWTs) have become ubiquitous for handling authentication and authorization. However, a subtle yet devastating vulnerability lurks within many JWT implementations: algorithm confusion attacks. This attack vector exploits the way servers validate JWT signatures, allowing attackers to forge tokens by manipulating the algorithm specified in the token header.

What is JWT Algorithm Confusion?

Algorithm confusion vulnerabilities typically arise due to flawed implementation of JWT libraries, where many libraries provide a single, algorithm-agnostic method for verifying signatures. The vulnerability occurs when a server fails to properly enforce which cryptographic algorithm should be used to verify a JWT’s signature.

Algorithm confusion occurs when a system fails to properly verify the type of signature used in a JWT, allowing an attacker to exploit insufficient distinction between different signing methods. The most dangerous variant involves switching from RS256 (RSA with SHA-256, an asymmetric algorithm) to HS256 (HMAC with SHA-256, a symmetric algorithm).

The Fundamental Difference Between RS256 and HS256

Understanding algorithm confusion requires grasping the fundamental difference between symmetric and asymmetric cryptography:

RS256 (RSA + SHA-256) - Asymmetric: - Uses a private key to sign tokens - Uses a mathematically related public key to verify signatures - The private key must be kept secret - The public key can be shared openly - Two different keys for two different purposes

HS256 (HMAC + SHA-256) - Symmetric: - Uses a single secret key for both signing and verification - The same key performs both operations - The secret must be kept confidential - Anyone with the secret can create and verify tokens

How the Attack Works: The Deadly Switch

The most common variant involves swapping an RS256 (RSA) token to HS256 (HMAC), and then using the RSA public key as the HMAC secret. Here’s the attack flow:

Step 1: Obtain a Valid JWT

The attacker first obtains a legitimate JWT from the application, typically signed with RS256:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIiwicm9sZSI6InVzZXIifQ.signature

This token has three parts: - Header: {"alg":"RS256","typ":"JWT"} - Payload: {"sub":"user123","role":"user"} - Signature: Verified using the server’s public RSA key

Step 2: Acquire the Public Key

Servers sometimes expose their public keys as JSON Web Key (JWK) objects via a standard endpoint mapped to /jwks.json or /.well-known/jwks.json. Attackers can obtain public keys through several methods:

  • Standard JWKS endpoints (/.well-known/jwks.json)
  • Server documentation or configuration files
  • SSL/TLS certificates
  • Deriving keys from multiple captured JWTs using tools like jwt_forgery.py

Step 3: Modify the Token Header and Payload

The attacker changes the algorithm in the header from RS256 to HS256 and modifies the payload to escalate privileges:

{
  "alg": "HS256",
  "typ": "JWT"
}
{
  "sub": "user123",
  "role": "admin"
}

Step 4: Sign with the Public Key as HMAC Secret

Forging tokens involves signing the token payload using the PEM-formatted public key as an HMAC key. The attacker signs the modified token using HS256, treating the server’s public RSA key as the HMAC secret.

Step 5: Server Accepts the Forged Token

When the server receives this malicious token, the vulnerability manifests in how the verification method processes it:

Problems arise when website developers assume the verify method will exclusively handle JWTs signed using an asymmetric algorithm like RS256, and always pass a fixed public key to the method.

// Vulnerable code pattern
publicKey = <public-key-of-server>;
token = request.getCookie("session");
verify(token, publicKey);

If the server receives a token signed using a symmetric algorithm like HS256, the library’s generic verify method will treat the public key as an HMAC secret. The server unwittingly uses its own public key as the HMAC secret to verify the attacker’s signature, which validates successfully because the attacker used the same public key to create the signature.

Real-World Impact: Recent Vulnerabilities

Algorithm confusion is not merely a theoretical threat. Recent discoveries demonstrate its continued prevalence:

CVE-2024-54150 (December 2024)

A vulnerability was discovered in the cjwt library where the function cjwt_decode does not require developers to pick an algorithm, and the code for handling HMAC and RSA keys was identical. The library’s switch statement would process HS256 tokens using the provided key without detecting that a public key was passed as a secret.

CVE-2024-37568 (2024)

This critical vulnerability in Authlib, a popular Python library for OAuth and OpenID Connect implementations, arose when the ‘alg’ claim was missing or incorrect, causing Authlib to incorrectly default to HMAC verification even when a public key was present.

CVE-2023-48238 (2023)

The json-web-token library was found vulnerable because the algorithm to use for verifying the signature was taken from the JWT token itself, which at that point was still unverified and shouldn’t be trusted.

Why This Vulnerability Exists: The Root Causes

Flawed Library Design

The JWT header contains an alg parameter that tells the server which algorithm was used to sign the token and which algorithm it needs to use when verifying the signature, creating an inherently flawed design because the server has no option but to implicitly trust user-controllable input from the token which hasn’t been verified.

Developer Misunderstanding

Many developers misunderstand the security implications of generic verification methods. They assume that passing a public key automatically means the library will only accept asymmetric signatures, not realizing that the library trusts the algorithm specified in the unverified token header.

Insufficient Validation

Applications often fail to validate that the algorithm in the token header matches their expected algorithm. Without this check, attackers can substitute algorithms at will.

Demonstrating the Attack: Practical Exploitation

Here’s how an attacker might execute this attack using common tools:

Using jwt_tool

# Extract the public key from JWKS endpoint
curl https://target.com/.well-known/jwks.json > jwks.json

# Create a forged token
python jwt_tool.py original_token.txt -X k -pk public_key.pem

Manual Python Implementation

import jwt
import base64

# Read the public key
with open('public_key.pem', 'r') as f:
    public_key = f.read()

# Create malicious payload
payload = {
    'sub': 'user123',
    'role': 'admin',
    'iat': 1234567890
}

# Sign using HS256 with public key as secret
forged_token = jwt.encode(
    payload,
    public_key,
    algorithm='HS256'
)

print(forged_token)

Using Burp Suite

Once you have the public key in a suitable format, you can modify the JWT however you like, ensuring that the alg header is set to HS256, then sign the token using the HS256 algorithm with the RSA public key as the secret.

Advanced Exploitation Techniques

Public Key Recovery

In cases where the public key isn’t readily available, you may still be able to test for algorithm confusion by deriving the key from a pair of existing JWTs using tools such as jwt_forgery.py.

The mathematical relationship between multiple signatures created with the same private key can be exploited to reconstruct the public key, requiring only two JWTs signed with the same key.

Format Sensitivity

The public key you use to sign the token must be absolutely identical to the public key stored on the server, including using the same format and preserving any non-printing characters like newlines. Attackers often need to experiment with different key formats:

  • PEM format with headers
  • Raw key material
  • Different line ending styles (CRLF vs LF)
  • With or without trailing newlines

Detection and Testing

Identifying Vulnerable Applications

Security researchers can test for algorithm confusion by:

  1. Analyzing JWT headers to identify the signing algorithm in use
  2. Locating public keys through JWKS endpoints or other means
  3. Attempting the attack by modifying the algorithm and signing with the public key
  4. Observing server behavior to see if the forged token is accepted

Automated Scanning

From Burp Suite Professional 2022.5.1, Burp Scanner can automatically detect a number of vulnerabilities in JWT mechanisms, including algorithm confusion attacks.

Defense Strategies: Protecting Your Applications

1. Explicitly Specify Expected Algorithms

JWT libraries should add an algorithm parameter to their verification function, and the server should already know what algorithm it uses to sign tokens.

Secure Implementation:

// Node.js with jsonwebtoken
jwt.verify(token, publicKey, { algorithms: ['RS256'] });
# Python with PyJWT
jwt.decode(token, public_key, algorithms=['RS256'])
// Java with jjwt
Jwts.parserBuilder()
    .setSigningKey(publicKey)
    .requireAlgorithm(SignatureAlgorithm.RS256)
    .build()
    .parseClaimsJws(token);

2. Use Algorithm Whitelisting

It is preferable to adopt a whitelist, explicitly defining the authorised algorithms such as HS256 or RS256, which guarantees strict control and avoids loopholes caused by lax interpretations of the algorithm.

3. Separate Verification Logic by Key Type

Never use the same verification method for both symmetric and asymmetric keys. Implement separate code paths:

function verifyToken(token, expectedAlgorithm, key) {
    // Extract algorithm from header for logging only
    const header = JSON.parse(
        Buffer.from(token.split('.')[0], 'base64').toString()
    );
    
    // NEVER trust the header algorithm
    // Always use the expected algorithm
    if (expectedAlgorithm === 'RS256') {
        return verifyRS256(token, key);
    } else if (expectedAlgorithm === 'HS256') {
        return verifyHS256(token, key);
    }
    
    throw new Error('Unsupported algorithm');
}

4. Implement Type Checking

Some libraries now include protections that detect when a public key is being used where a symmetric secret is expected:

function isPublicKey(key) {
    return key.includes('BEGIN PUBLIC KEY') || 
           key.includes('BEGIN RSA PUBLIC KEY');
}

function verifyHS256(token, secret) {
    if (isPublicKey(secret)) {
        throw new Error('Public key cannot be used as HMAC secret');
    }
    return jwt.verify(token, secret, { algorithms: ['HS256'] });
}

5. Reject the “none” Algorithm

The none algorithm is intended to be used for situations where the integrity of the token has already been verified, but unfortunately some libraries treated tokens signed with the none algorithm as a valid token with a verified signature.

Always explicitly reject tokens using the “none” algorithm in production:

const decoded = jwt.verify(token, key, {
    algorithms: ['RS256', 'HS256'],
    // The 'none' algorithm is automatically rejected
});

6. Use Strong Secrets for HMAC

When implementing JWT applications, developers sometimes make mistakes like forgetting to change default or placeholder secrets, making it trivial for an attacker to brute-force a server’s secret using a wordlist of well-known secrets.

For HS256 implementations: - Use cryptographically random secrets of at least 256 bits - Store secrets securely in environment variables or secret management systems - Rotate secrets regularly - Never hardcode secrets in source code

7. Validate All Claims Thoroughly

Beyond algorithm validation, implement comprehensive claim validation:

jwt.verify(token, publicKey, {
    algorithms: ['RS256'],
    issuer: 'https://trusted-issuer.com',
    audience: 'your-application',
    clockTolerance: 60 // seconds of leeway for time comparisons
});

8. Keep Libraries Updated

To address CVE-2024-37568, it is imperative to upgrade Authlib to version 1.3.1 or later, which incorporates robust fixes to prevent algorithm confusion and ensure correct JWT validation.

Regularly update JWT libraries to benefit from security patches and improved validation logic.

Best Practices for JWT Security

Comprehensive Security Checklist

Algorithm Management: - ✅ Explicitly specify allowed algorithms in verification calls - ✅ Never trust the algorithm from the token header - ✅ Disable the “none” algorithm in production - ✅ Use asymmetric algorithms (RS256, ES256) for most use cases - ✅ Implement algorithm whitelisting at the application level

Key Management: - ✅ Store private keys securely using key management services - ✅ Use strong, randomly generated secrets for HMAC - ✅ Rotate keys regularly with grace periods for key rollover - ✅ Keep public keys accessible but protect private keys - ✅ Never embed keys in source code or configuration files

Token Handling: - ✅ Set short expiration times (15 minutes or less for access tokens) - ✅ Implement refresh token rotation - ✅ Validate all standard claims (iss, aud, exp, nbf, iat) - ✅ Use HTTPS exclusively for token transmission - ✅ Store tokens securely (HttpOnly cookies for web apps)

Implementation: - ✅ Use well-maintained, up-to-date JWT libraries - ✅ Implement proper error handling without leaking information - ✅ Log security events for monitoring and incident response - ✅ Perform regular security audits and penetration testing - ✅ Educate developers on JWT security principles

Selecting the Right Algorithm

For New Applications: - RS256 or ES256: Best for most scenarios requiring public key distribution - PS256: Enhanced security over RS256 with probabilistic signatures - EdDSA: Most secure and efficient, excellent for new implementations

Avoid: - HS256 for public APIs where the secret might be exposed - Weak keys: Minimum 2048 bits for RSA, 256 bits for ECDSA - Algorithm flexibility: Don’t support multiple algorithms unless absolutely necessary

Testing Your Implementation

Vulnerability Assessment Steps

  1. Review verification code to ensure algorithms are explicitly specified
  2. Test with modified tokens where the algorithm is changed
  3. Attempt public key substitution to verify protections are in place
  4. Check for “none” algorithm acceptance in all environments
  5. Verify key format sensitivity doesn’t affect security

Security Testing Tools

  • jwt_tool: Comprehensive JWT testing toolkit
  • Burp Suite: Web application security testing with JWT extensions
  • OWASP ZAP: Open-source security scanner with JWT support
  • Custom scripts: Python or Node.js scripts for specific test cases

The Broader Context: JWT Security Landscape

Algorithm confusion is just one of several JWT vulnerabilities that applications must defend against:

  • Weak secrets: Brute-forcing HMAC secrets
  • Missing signature verification: Accepting unsigned tokens
  • Token leakage: Exposure through logs, URLs, or client-side storage
  • Replay attacks: Reusing captured tokens
  • JKU/X5U injection: Header parameter manipulation
  • Kid injection: Path traversal through key identifier

JWTs are not secure just because they are JWTs; it’s the way in which they’re used that determines whether they are secure or not.

Conclusion: Vigilance is Essential

JWT algorithm confusion represents a critical security vulnerability that has affected major libraries and continues to surface in new implementations. The attack exploits a fundamental trust issue: allowing unverified tokens to dictate how they should be verified.

Never trust the “alg” field from the JWT itself, and enforce the expected algorithm at the configuration level. By implementing explicit algorithm validation, using algorithm-specific verification methods, and following security best practices, developers can protect their applications from this devastating attack vector.

The key takeaway: Never trust user input, especially when it determines security-critical operations. The algorithm field in a JWT header is user-controlled data from an unverified source. Treat it with the same skepticism you would treat any other untrusted input, and always enforce your security policy explicitly in code.

As authentication mechanisms continue to evolve, staying informed about vulnerabilities like algorithm confusion and implementing defense-in-depth strategies remains essential for maintaining application security. Regular security audits, developer education, and proactive monitoring are the cornerstones of a robust JWT security posture.


Resources for Further Learning:

  • OWASP JWT Security Cheat Sheet
  • RFC 7519: JSON Web Token (JWT)
  • RFC 8725: JWT Best Current Practices
  • PortSwigger Web Security Academy: JWT Attacks
  • Burp Suite JWT Testing Tools

Stay secure, verify explicitly, and never trust the algorithm header! 🔒

Related Topics

#JWT algorithm confusion, JWT vulnerability, RS256 to HS256 attack, JWT token forgery, JSON Web Token exploit, JWT signature bypass, JWT security, JWT misconfiguration, JWT hacking, JWT validation flaw, JWT RS256 vulnerability, JWT HS256 exploit, JWT header tampering, JWT signature confusion, JWT public key attack, JWT asymmetric to symmetric, JWT authentication bypass, JWT algorithm spoofing, JWT attack 2025, JWT cryptography flaw, JWT pen testing, JWT bug bounty, JWT algorithm confusion example, JWT security misconfiguration, RS256 vs HS256, JWT verification flaw, JWT exploit tutorial, JWT manipulation, JWT injection, JWT cracking, JWT signature validation, JWT best practices, JWT secure implementation, JWT token validation, JWT algorithm confusion prevention, JWT library vulnerability, JWT attack detection, JWT forge token, JWT public key secret, JWT exploit chain, JWT hack demo, JWT algorithm confusion CVE, JWT exploit research, JWT misused algorithms, JWT bypass authentication, JWT implementation flaws, JWT pentesting techniques, JWT header attack, JWT payload tampering, RS256 public key secret, HS256 confusion exploit, JWT asymmetric signature vulnerability, JWT algorithm downgrade, JWT secure verification, JWT exploit mitigation

Share this article

More InstaTunnel Insights

Discover more tutorials, tips, and updates to help you build better with localhost tunneling.

Browse All Articles