A single line of configuration can forge a JWT. Here is the technical analysis of the 'none' algorithm attack, and the automated static analysis guard that eliminates this architectural risk.
JWT authentication is everywhere. It's also one of the most misconfigured security mechanisms.
One line of code can compromise everything.
// ❌ This looks fine...
const decoded = jwt.verify(token, secret, {
algorithms: ["HS256", "none"], // 💀 The vulnerability
});
// 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
| CVE | Library | Impact |
|---|---|---|
| CVE-2015-2951 | jwt-simple | Algorithm confusion |
| CVE-2016-10555 | jose2go | None algorithm bypass |
| CVE-2018-0114 | node-jose | Key confusion |
// ✅ Explicitly whitelist algorithms
const decoded = jwt.verify(token, secret, {
algorithms: ["HS256"], // Only what you use!
});
// ❌ Dangerous
jwt.verify(token, secret, { algorithms: ["none"] });
// ✅ Safe
jwt.verify(token, secret, { algorithms: ["HS256"] });
// ❌ Dangerous: RS256 token verified with symmetric secret
jwt.verify(token, publicKey);
// ✅ Safe: Explicit algorithm
jwt.verify(token, publicKey, { algorithms: ["RS256"] });
// ❌ Dangerous: Brute-forceable
jwt.sign(payload, "password123");
// ✅ Safe: Strong secret
jwt.sign(payload, process.env.JWT_SECRET); // 256+ bits
// ❌ Dangerous: Token valid forever
jwt.sign({ userId: 123 }, secret);
// ✅ Safe: Short expiration
jwt.sign({ userId: 123 }, secret, { expiresIn: "1h" });
// ❌ Dangerous: Password in token (tokens can be decoded!)
jwt.sign({ userId: 123, password: "secret" }, key);
// ✅ Safe: Only IDs
jwt.sign({ userId: 123 }, key);
// eslint.config.js
import jwtPlugin from "eslint-plugin-jwt";
export default [jwtPlugin.configs.recommended];
| 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 |
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']
::dev-to-cta{url="https://npmjs.com/package/eslint-plugin-jwt in 60 seconds. 13 rules. Full JWT security. Zero false positives.**
📦 npm: eslint-plugin-jwt 📖 Rule: no-algorithm-none
The Interlace ESLint Ecosystem Interlace is a high-fidelity suite of static code analyzers designed to automate security, performance, and reliability for the modern Node.js stack. With over 330 rules across 18 specialized plugins, it provides 100% coverage for OWASP Top 10, LLM Security, and Database Hardening.
Explore the full Documentation
© 2026 Ofri Peretz. All rights reserved.
Build Securely. I'm Ofri Peretz, a Security Engineering Leader and the architect of the Interlace Ecosystem. I build static analysis standards that automate security and performance for Node.js fleets at scale.
