Back to Blog
critical SEVERITY6 min read

JWT Algorithm Confusion: How a Missing Parameter Can Compromise Authentication

A critical authentication vulnerability was discovered where the jsonwebtoken library was being used without explicitly specifying allowed algorithms during token verification. This oversight enables attackers to exploit algorithm confusion attacks, potentially forging valid tokens by manipulating the algorithm header to 'none' or switching from asymmetric to symmetric algorithms, completely bypassing authentication controls.

O
By Orbis AppSec
Published March 6, 2026Reviewed June 3, 2026

Answer Summary

JWT algorithm confusion (CWE-347) is a critical authentication bypass vulnerability in Node.js applications using the jsonwebtoken library. It occurs when `jwt.verify()` is called without specifying the `algorithms` parameter, allowing attackers to manipulate the JWT header's `alg` field to 'none' (disabling signature verification) or switch from RS256 to HS256 (using the public key as HMAC secret). The fix is to explicitly whitelist allowed algorithms: `jwt.verify(token, secret, { algorithms: ['RS256'] })` to prevent token forgery and authentication bypass.

Vulnerability at a Glance

cweCWE-347 (Improper Verification of Cryptographic Signature)
fixExplicitly specify allowed algorithms in verification options: { algorithms: ['RS256'] }
riskComplete authentication bypass allowing attackers to forge valid tokens and impersonate any user
languageNode.js (JavaScript)
root causeMissing `algorithms` parameter in jwt.verify() allows attacker-controlled algorithm selection
vulnerabilityJWT Algorithm Confusion / Algorithm Substitution Attack

Introduction

JSON Web Tokens (JWT) have become the de facto standard for stateless authentication in modern web applications. However, their security depends entirely on proper implementation. A recently patched vulnerability in an application using the jsonwebtoken library (^9.0.2) highlights a common but dangerous oversight: failing to specify allowed algorithms during token verification.

This vulnerability, while seemingly minor in code terms, can have catastrophic consequences—allowing attackers to completely bypass authentication and impersonate any user in your system. If you're using JWTs in your application, this is a must-read.

The Vulnerability Explained

What is Algorithm Confusion?

JWT tokens consist of three parts: a header, a payload, and a signature. The header specifies which cryptographic algorithm was used to sign the token. When verifying a JWT, the server must check both the signature's validity AND ensure the algorithm used is one that's expected and secure.

The vulnerability occurs when jwt.verify() is called without the algorithms parameter:

// VULNERABLE CODE
const decoded = jwt.verify(token, secretOrPublicKey);

Without explicit algorithm specification, the library will trust whatever algorithm is declared in the token's header. This opens two critical attack vectors:

Attack Vector 1: The "None" Algorithm Attack

An attacker can modify a valid JWT's header to use the "none" algorithm, which indicates no signature verification is required:

{
  "alg": "none",
  "typ": "JWT"
}

When the server attempts to verify this token without algorithm restrictions, it may accept the token as valid without checking any signature at all. The attacker simply needs to:

  1. Decode an existing JWT
  2. Change the algorithm header to "none"
  3. Remove the signature portion
  4. Re-encode the token

Result: Complete authentication bypass.

Attack Vector 2: RS256 to HS256 Confusion

Many applications use asymmetric algorithms (like RS256) where tokens are signed with a private key and verified with a public key. If algorithm validation is missing, an attacker can:

  1. Obtain the public key (often publicly accessible)
  2. Create a new token signed with HS256 (symmetric algorithm)
  3. Use the public key as the "secret" for HMAC signing

The server, expecting RS256 but not enforcing it, will use the public key to verify the HMAC signature—which the attacker created using that same public key.

Result: Token forgery with publicly available information.

Real-World Impact

This vulnerability allows attackers to:

  • Impersonate any user by forging tokens with arbitrary user IDs
  • Escalate privileges by adding admin roles to forged tokens
  • Bypass authentication entirely using the "none" algorithm
  • Access sensitive data belonging to other users
  • Perform unauthorized actions on behalf of legitimate users

Example Attack Scenario

Consider an e-commerce application:

  1. Alice registers as a regular user and receives a JWT
  2. Attacker intercepts or obtains Alice's token
  3. Attacker decodes the token and changes the header to "alg": "none"
  4. Attacker modifies the payload: {"userId": "admin", "role": "administrator"}
  5. Attacker re-encodes without a signature
  6. Server verifies token without algorithm check and grants admin access

The attacker now has full administrative control over the application.

The Fix

What Changed

The fix involves adding the algorithms parameter to all jwt.verify() calls throughout the application:

// BEFORE (Vulnerable)
const decoded = jwt.verify(token, secretOrPublicKey);

// AFTER (Secure)
const decoded = jwt.verify(token, secretOrPublicKey, {
  algorithms: ['RS256']  // Explicitly whitelist allowed algorithms
});

How It Solves the Problem

By specifying algorithms: ['RS256'] (or whichever algorithm your application uses), the verification process now:

  1. Rejects "none" algorithm tokens immediately, preventing signature bypass
  2. Prevents algorithm confusion by refusing tokens signed with unexpected algorithms
  3. Enforces cryptographic integrity by ensuring only your chosen secure algorithm is accepted

If an attacker tries to submit a token with "alg": "none" or "alg": "HS256" when only RS256 is allowed, the verification will fail with an error:

JsonWebTokenError: invalid algorithm

Complete Implementation Example

Here's a comprehensive example of secure JWT verification:

const jwt = require('jsonwebtoken');
const fs = require('fs');

// Load your keys securely (example for RS256)
const publicKey = fs.readFileSync('public-key.pem');

function verifyToken(token) {
  try {
    const decoded = jwt.verify(token, publicKey, {
      algorithms: ['RS256'],           // Whitelist allowed algorithms
      issuer: 'your-app.com',          // Verify token issuer
      audience: 'your-api',            // Verify intended audience
      maxAge: '1h'                     // Enforce expiration
    });

    return decoded;
  } catch (error) {
    if (error.name === 'TokenExpiredError') {
      throw new Error('Token has expired');
    }
    if (error.name === 'JsonWebTokenError') {
      throw new Error('Invalid token');
    }
    throw error;
  }
}

// Express middleware example
function authenticateJWT(req, res, next) {
  const authHeader = req.headers.authorization;

  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'No token provided' });
  }

  const token = authHeader.substring(7);

  try {
    req.user = verifyToken(token);
    next();
  } catch (error) {
    return res.status(403).json({ error: error.message });
  }
}

Prevention & Best Practices

1. Always Specify Allowed Algorithms

Never call jwt.verify() without the algorithms parameter:

// ❌ NEVER DO THIS
jwt.verify(token, secret);

// ✅ ALWAYS DO THIS
jwt.verify(token, secret, { algorithms: ['HS256'] });

2. Use Strong, Appropriate Algorithms

  • HS256/HS512: Suitable for symmetric scenarios where both signing and verification happen on the same server
  • RS256/RS512: Recommended for distributed systems where tokens are signed by one service and verified by multiple services
  • ES256/ES512: Modern elliptic curve algorithms offering better performance with equivalent security

Avoid: HS256 with RS256 public keys, any "none" algorithm, or weak algorithms like HS1.

3. Implement Defense in Depth

Algorithm specification is just one layer. Also implement:

const decoded = jwt.verify(token, secret, {
  algorithms: ['RS256'],
  issuer: 'trusted-issuer',      // Verify token source
  audience: 'your-service',      // Verify token destination
  maxAge: '15m',                 // Short expiration
  clockTolerance: 30             // Allow 30s clock skew
});

// Additional validation
if (!decoded.sub || !decoded.role) {
  throw new Error('Invalid token structure');
}

4. Use Security Linting Tools

Configure ESLint with security plugins to catch these issues:

{
  "plugins": ["security"],
  "rules": {
    "security/detect-unsafe-jwt": "error"
  }
}

5. Regular Security Audits

  • Automated scanning: Use tools like npm audit, Snyk, or OWASP Dependency-Check
  • Code review: Ensure all JWT operations are reviewed by security-aware developers
  • Penetration testing: Include JWT attack vectors in security assessments

6. Keep Dependencies Updated

The jsonwebtoken library has evolved to address security concerns. Always use the latest stable version:

npm update jsonwebtoken
npm audit fix

Security Standards References

  • CWE-347: Improper Verification of Cryptographic Signature
  • CWE-327: Use of a Broken or Risky Cryptographic Algorithm
  • OWASP Top 10 2021: A02:2021 – Cryptographic Failures
  • OWASP JWT Cheat Sheet: Comprehensive guide to JWT security
  • RFC 7519: JSON Web Token specification

Conclusion

The JWT algorithm confusion vulnerability demonstrates that security often fails not from complex exploits, but from simple oversights in implementation. A single missing parameter—algorithms—can completely undermine your authentication system.

Key Takeaways:

  1. Always specify allowed algorithms when verifying JWTs
  2. Use asymmetric algorithms (RS256) for distributed systems
  3. Implement multiple layers of validation beyond just signature verification
  4. Keep security libraries updated and monitor for vulnerabilities
  5. Audit your codebase specifically for JWT verification calls

If you're using the jsonwebtoken library, review your codebase today. Search for all instances of jwt.verify() and ensure they include the algorithms parameter. This simple fix could be the difference between a secure application and a compromised one.

Remember: in security, what you don't specify can hurt you as much as what you do. Write defensive code, assume malicious input, and always validate explicitly.

Have you checked your JWT implementation? Share your experiences or questions in the comments below.


Stay secure, and happy coding!

Frequently Asked Questions

What is JWT algorithm confusion?

JWT algorithm confusion is an authentication bypass vulnerability where an attacker manipulates the algorithm field in a JWT header to either disable signature verification ('none' algorithm) or switch from asymmetric to symmetric algorithms (RS256 to HS256), allowing them to forge valid tokens without knowing the private key.

How do you prevent JWT algorithm confusion in Node.js?

Always explicitly specify the allowed algorithms when calling jwt.verify() by including the algorithms parameter: `jwt.verify(token, secret, { algorithms: ['RS256'] })`. Never allow the 'none' algorithm in production, and never let the token header dictate which algorithm to use for verification.

What CWE is JWT algorithm confusion?

JWT algorithm confusion is classified as CWE-347 (Improper Verification of Cryptographic Signature). It may also relate to CWE-327 (Use of a Broken or Risky Cryptographic Algorithm) when the 'none' algorithm is accepted.

Is using strong secrets enough to prevent JWT algorithm confusion?

No. Even with strong secrets and properly generated RS256 key pairs, failing to specify allowed algorithms in jwt.verify() leaves your application vulnerable. An attacker can switch the algorithm to 'none' or HS256 without needing to crack your secret, completely bypassing the signature verification regardless of secret strength.

Can static analysis detect JWT algorithm confusion?

Yes. Static analysis tools like Semgrep can detect jwt.verify() calls missing the algorithms parameter. Pattern-based scanners can identify this vulnerability by looking for verification calls without explicit algorithm whitelisting, making it an ideal candidate for automated security scanning.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #59

Related Articles

high

How Authentication Bypass happens in PyJWT and how to fix it

A critical authentication bypass vulnerability in PyJWT 2.12.1 allowed attackers to forge valid JSON Web Tokens, potentially bypassing application authentication mechanisms entirely. The vulnerability was fixed in PyJWT 2.13.0 through security improvements to token validation logic. This fix is essential for any application relying on JWT-based authentication.

medium

JWT Authentication Vulnerability: How Weak Token Validation Exposed Dashboard APIs

A critical authentication bypass vulnerability was discovered in a dashboard application where JWT tokens could be forged due to improper validation. The vulnerability affected multiple routes including backup, live chat, and authentication endpoints, potentially allowing attackers to access sensitive operations without proper authorization. This fix demonstrates why robust JWT validation is essential for API security.

critical

Path Traversal in node-tar: How Hardlink Bypass Exposed Your Files

A medium-severity vulnerability (CVE-2026-24842) in node-tar allowed attackers to bypass hardlink security checks and create arbitrary files through path traversal attacks. This vulnerability, combined with improper configuration management storing JWT secrets in plaintext .env files, created a dangerous attack vector for token forgery and unauthorized access.

critical

How buffer overflow in memcpy() happens in Node.js N-API bindings and how to fix it

A critical buffer overflow vulnerability was discovered in the GetBufferAsVector() function in examples_nodejs/src/zupt_napi.cpp, where memcpy() copied data from JavaScript Uint8Array buffers without proper bounds validation. This vulnerability could allow attackers to trigger memory corruption by providing maliciously crafted input arrays to the native Node.js module, potentially leading to crashes or arbitrary code execution.

critical

How buffer overflow happens in C sprintf() and how to fix it

A critical buffer overflow vulnerability was discovered in the ArrowTest() function in main/main.c, where sprintf() was writing formatted strings to a 24-byte buffer without bounds checking. By replacing sprintf() with snprintf() and specifying the buffer size, the vulnerability was eliminated, preventing attackers from corrupting heap memory through oversized width or height parameters.

critical

How buffer overflow happens in C libficus.c sprintf() and how to fix it

A buffer overflow vulnerability was discovered in `runtime/ficus/impl/libficus.c` where `sprintf()` was used to write a formatted compiler version string into a fixed-size stack buffer without any bounds checking. The fix replaces both vulnerable `sprintf()` calls with `snprintf()`, passing `sizeof(cver)` as the maximum write length to ensure the buffer can never be overrun. This change eliminates the risk of stack memory corruption that could be triggered by an attacker with control over the bu