JWT 算法 “none” 攻击:仅一行代码的漏洞
发布: (2025年12月31日 GMT+8 13:53)
3 min read
原文: Dev.to
Source: Dev.to
攻击演示
// ❌ This looks fine...
const decoded = jwt.verify(token, secret, {
algorithms: ['HS256', 'none'], // 💀 The vulnerability
});
1. 攻击者获取一个有效的 JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTYiLCJyb2xlIjoidXNlciJ9.
signature_here
2. 将头部修改为 none 算法
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.
eyJzdWIiOiIxMjM0NTYiLCJyb2xlIjoiYWRtaW4ifQ.
// No signature needed!
3. 服务器因为在算法列表中包含 "none" 而接受了它
攻击者现在是管理员。
已知 CVE
| CVE | 库 | 问题 |
|---|---|---|
| CVE-2015-2951 | jwt‑simple | 算法混淆 |
| CVE-2016-10555 | jose2go | none 算法绕过 |
| CVE-2018-0114 | node‑jose | 密钥混淆 |
安全配置示例
// ✅ 明确白名单算法
const decoded = jwt.verify(token, secret, {
algorithms: ['HS256'], // 只使用你需要的!
});
// ❌ 危险:允许 "none"
jwt.verify(token, secret, { algorithms: ['none'] });
// ✅ 安全:显式 RS256 验证
jwt.verify(token, publicKey, { algorithms: ['RS256'] });
// ❌ 危险:可被暴力破解的密钥
jwt.sign(payload, 'password123');
// ✅ 安全:强密钥(256+ 位)
jwt.sign(payload, process.env.JWT_SECRET);
// ❌ 危险:令牌永不过期
jwt.sign({ userId: 123 }, secret);
// ✅ 安全:短期过期时间
jwt.sign({ userId: 123 }, secret, { expiresIn: '1h' });
// ❌ 危险:在令牌中存放密码(令牌可以被解码!)
jwt.sign({ userId: 123, password: 'secret' }, key);
// ✅ 安全:仅在负载中放置 ID
jwt.sign({ userId: 123 }, key);
JWT 安全的 ESLint 插件
eslint-plugin-jwt 规则集强制最佳实践并捕获常见陷阱。
规则概览
| 规则 | CWE | 检测内容 |
|---|---|---|
no-algorithm-none | CWE‑347 | 允许 "none" 算法 |
no-algorithm-confusion | CWE‑327 | RS/HS 混淆攻击 |
no-weak-secret | CWE‑326 | 可被暴力破解的密钥 |
no-hardcoded-secret | CWE‑798 | 代码中硬编码的密钥 |
no-sensitive-payload | CWE‑312 | 令牌中包含个人敏感信息 |
require-expiration | CWE‑613 | 缺失 exp 声明 |
require-algorithm-whitelist | CWE‑327 | 未显式指定算法列表 |
require-issuer-validation | CWE‑345 | 缺失 iss 校验 |
require-audience-validation | CWE‑345 | 缺失 aud 校验 |
no-decode-without-verify | CWE‑347 | 错误使用 jwt.decode() |
require-issued-at | CWE‑613 | 缺失 iat 声明 |
require-max-age | CWE‑613 | verify 中缺少 maxAge 参数 |
no-timestamp-manipulation | CWE‑345 | 时钟偏移利用 |
示例 lint 错误
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']
安装
npm install eslint-plugin-jwt
// .eslintrc.js
import jwtPlugin from 'eslint-plugin-jwt';
export default [jwtPlugin.configs.recommended];
13 条规则。完整的 JWT 安全。零误报。
⭐ 在 GitHub 上点星。
立即行动
🚀 现在检查你的 JWT 配置。你的算法列表中是否包含 "none"?