DEV Community

Cover image for The JWT Algorithm "none" Attack: The Vulnerability in 1 Line of Code
Ofri Peretz
Ofri Peretz

Posted on

The JWT Algorithm "none" Attack: The Vulnerability in 1 Line of Code

JWT authentication is everywhere. It's also one of the most misconfigured security mechanisms.

One line of code can compromise everything.

The Vulnerable Code

// ❌ This looks fine...
const decoded = jwt.verify(token, secret, {
  algorithms: ['HS256', 'none'], // πŸ’€ The vulnerability
});
Enter fullscreen mode Exit fullscreen mode

The Attack

// 1. Attacker takes a valid JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTYiLCJyb2xlIjoidXNlciJ9.
signature_here

// 2. Modifies the header to use "none" algorithm:
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.
eyJzdWIiOiIxMjM0NTYiLCJyb2xlIjoiYWRtaW4ifQ.
// No signature needed!

// 3. Server accepts it because "none" is in algorithms list
// Attacker is now admin
Enter fullscreen mode Exit fullscreen mode

Real CVEs

CVE Library Impact
CVE-2015-2951 jwt-simple Algorithm confusion
CVE-2016-10555 jose2go None algorithm bypass
CVE-2018-0114 node-jose Key confusion

The Fix

// βœ… Explicitly whitelist algorithms
const decoded = jwt.verify(token, secret, {
  algorithms: ['HS256'], // Only what you use!
});
Enter fullscreen mode Exit fullscreen mode

All JWT Vulnerabilities

1. Algorithm None

// ❌ Dangerous
jwt.verify(token, secret, { algorithms: ['none'] });

// βœ… Safe
jwt.verify(token, secret, { algorithms: ['HS256'] });
Enter fullscreen mode Exit fullscreen mode

2. Algorithm Confusion

// ❌ Dangerous: RS256 token verified with symmetric secret
jwt.verify(token, publicKey);

// βœ… Safe: Explicit algorithm
jwt.verify(token, publicKey, { algorithms: ['RS256'] });
Enter fullscreen mode Exit fullscreen mode

3. Weak Secret

// ❌ Dangerous: Brute-forceable
jwt.sign(payload, 'password123');

// βœ… Safe: Strong secret
jwt.sign(payload, process.env.JWT_SECRET); // 256+ bits
Enter fullscreen mode Exit fullscreen mode

4. Missing Expiration

// ❌ Dangerous: Token valid forever
jwt.sign({ userId: 123 }, secret);

// βœ… Safe: Short expiration
jwt.sign({ userId: 123 }, secret, { expiresIn: '1h' });
Enter fullscreen mode Exit fullscreen mode

5. Sensitive Payload

// ❌ Dangerous: Password in token (tokens can be decoded!)
jwt.sign({ userId: 123, password: 'secret' }, key);

// βœ… Safe: Only IDs
jwt.sign({ userId: 123 }, key);
Enter fullscreen mode Exit fullscreen mode

ESLint Coverage

// eslint.config.js
import jwtPlugin from 'eslint-plugin-jwt';

export default [jwtPlugin.configs.recommended];
Enter fullscreen mode Exit fullscreen mode

13 JWT Rules

Rule CWE What it catches
no-algorithm-none CWE-347 Algorithm "none" allowed
no-algorithm-confusion CWE-327 RS/HS confusion attacks
no-weak-secret CWE-326 Brute-forceable secrets
no-hardcoded-secret CWE-798 Secrets in code
no-sensitive-payload CWE-312 PII in tokens
require-expiration CWE-613 Missing exp claim
require-algorithm-whitelist CWE-327 No explicit algorithms
require-issuer-validation CWE-345 Missing iss check
require-audience-validation CWE-345 Missing aud check
no-decode-without-verify CWE-347 jwt.decode() misuse
require-issued-at CWE-613 Missing iat claim
require-max-age CWE-613 No maxAge in verify
no-timestamp-manipulation CWE-345 Clock skew exploits

Error Messages

src/auth.ts
  15:3  error  πŸ”’ CWE-347 CVSS:9.8 | JWT algorithm 'none' is allowed
               Risk: Attackers can forge tokens without a signature
               Fix: Remove 'none' from algorithms: ['HS256']
Enter fullscreen mode Exit fullscreen mode

Quick Install

πŸ“¦ npm install eslint-plugin-jwt

import jwtPlugin from 'eslint-plugin-jwt';
export default [jwtPlugin.configs.recommended];
Enter fullscreen mode Exit fullscreen mode

13 rules. Full JWT security. Zero false positives.


πŸ“¦ npm: eslint-plugin-jwt
πŸ“– Rule: no-algorithm-none

⭐ Star on GitHub


πŸš€ Check your JWT config now. Is "none" in your algorithms?

GitHub | LinkedIn

Top comments (0)