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 orbisai0security
March 6, 2026
#jwt#authentication#security#node.js#jsonwebtoken#algorithm-confusion#cryptography

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!

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #59

Related Articles

critical

Stack Buffer Overflow in MapScale: How Five Unsafe sprintf Calls Created a Critical Vulnerability

A critical stack-based buffer overflow vulnerability was discovered and patched in `src/mapscale.c`, where five unbounded `sprintf` calls wrote formatted output into fixed-size stack buffers without any bounds checking. An attacker controlling unit text strings could overflow the stack buffer, potentially overwriting the function return address and achieving arbitrary code execution. The fix replaces dangerous `sprintf` calls with their bounds-checked counterparts, eliminating the overflow risk

critical

Heap Buffer Overflows in YAML Parser: How Unchecked memcpy Calls Create Critical Attack Vectors

A critical heap buffer overflow vulnerability was discovered and patched in the YAML parser embedded within an Android VPN application, where five unvalidated `memcpy` calls could allow an attacker to corrupt heap memory by supplying a crafted YAML configuration file. This class of vulnerability is particularly dangerous because it can lead to arbitrary code execution or application crashes in security-sensitive contexts. The fix adds proper bounds validation before each copy operation, eliminat

critical

Critical Buffer Overflow Fixed: When "Safe" Functions Aren't Safe

A critical vulnerability in DeepSkyStackerKernel's StackWalker.cpp was silently replacing bounds-checking string functions with their unsafe counterparts via preprocessor macros, exposing the entire codebase to buffer overflow attacks. This fix removes the dangerous macro definitions that discarded buffer size arguments, restoring the intended memory safety protections across all call sites. Understanding how this subtle macro trick works is essential for any C/C++ developer working with string