JWT 安全最佳实践|alg none 攻击 / 过期 / 签名验证
JWT(JSON Web Token)是现代 Web API 身份验证的事实标准,但在误解规范的情况下实现它很容易引入漏洞。本文解释了常见的 JWT 误用模式和避免这些模式的最佳实践。
JWT 结构回顾
JWT 是一种用点连接三个 Base64URL 编码字符串的格式。
eyJhbGci... . eyJzdWIi... . SflKxwRJSM...
header payload signature
- header:签名算法(
alg)和令牌类型(typ) - payload:声明(sub / iss / aud / exp / iat / ...)的 JSON 对象
- signature:对 header 和 payload 的 HMAC / RSA / ECDSA 签名
您可以使用 DevLab 的 JWT 解码器验证 JWT 的内容。请记住 Base64URL 是编码而不是加密,因此有效负载内容可以被任何人读取,而无需服务器签名验证。
威胁 1: alg=none 攻击
JWT 规范包括 "alg": "none",一个"无签名"算法指定。如果攻撃者利用这个用 {"alg":"none"} 头修改有效负载,并且库原样接受它,就会成为允许冒充任何用户的严重漏洞。
对策:
- 在验证时明确地将允许的算法列入白名单
- 像
jwt.verify(token, secret, { algorithms: ['HS256'] })一样作为数组传递 - 2015年左右的库存在漏洞,但当前的主流库已经进行了修复。不过,除非自己开发,否则应该始终使用最新版本。
// ✗ 悪い例 (アルゴリズム未指定 = ライブラリが alg ヘッダを信頼)
jwt.verify(token, secret);
// ✓ 良い例 (アルゴリズムを固定)
jwt.verify(token, secret, { algorithms: ['HS256'] });
威胁 2: 密钥混同攻击 (key confusion)
拥有 RSA 公钥的攻击者可以通过 将算法从 RS256 改为 HS256 来强制将公钥作为"秘密密钥"处理。如果 JWT 库在选择验证函数时信任 alg 字段,用公钥签署的 HMAC 伪造令牌将被接受。
对策:必须在验证端固定算法(与威胁 1 相同的对策)。此外,还要明确区分密钥类型:
- 对于 HS256,以
Buffer形式传递 - 对于 RS256,以 PEM 格式传递公钥
威胁 3: 未验证的过期时间 / 无期限令牌
exp JWT 中的声明以 UNIX 秒表示过期时间,但在验证期间经常被忽略。如果一旦颁发的令牌可以无限期使用,则无法遏制泄露造成的损害。
对策:
- 发布时将
exp设置为较短的期限(访问令牌建议设置为15分钟到1小时) - 验证时必须检查
exp(主要库自动执行) - 对于长期会话,使用刷新令牌模式(短期访问令牌+长期刷新令牌+服务器端撤销列表)
威胁 4: 在 JWT 中存储敏感信息
JWT 有效负载只是 Base64 编码,任何人都可以对其进行解码。尽管如此,将密码或信用卡号放在有效负载中的实现仍在继续出现。
对策:
- payload 中仅包含表示「这是该用户」的最少识别信息 (sub / user_id / role)
- 将敏感信息存储在服务器端数据库中,从 JWT 使用 user_id 检索
- 如果必须通过JWT发送敏感信息,请使用 JWE (加密JWT)
威胁 5: 无法撤销 (logout)
当 JWT 以无状态方式颁发时,无法撤销。即使用户注销,由于服务器没有令牌信息,令牌"可以使用到过期"。即使更改密码,已颁发的 JWT 仍然有效。
对策:
- 使用短的
exp(15分钟或更少) 来最小化损害窗口 - 登出时将
jti(JWT ID) 注册到服务器端的 失效列表 (denylist),验证时进行对照 - 对于重要事件(密码更改、权限更改),将
token_version存储在用户记录中,并在验证时确认匹配
威胁 6:弱秘密
如果 HS256 密钥太短,可以通过暴力破解。特别是像 "secret" 和 "password123" 这样的字符串非常脆弱。
对策:
- HS256 应使用至少 256 位 (32 字节) 的随机值
- 使用
openssl rand -base64 32或密码生成工具生成 - 绝对不要向 Git 提交秘密。用环境变量或 Secrets Manager 管理
最佳实践总结
- 在验证端固定算法 (
algorithms: ['HS256']) - HS256 密钥应为 256 位或更长,RS256 应使用 2048 位或更长的 RSA
- 访问令牌的
exp是15分钟到1小时 - 使用刷新令牌模式实现长期会话
- 不要在 payload 中包含敏感信息(Base64 不是加密)
- 登出时在失效列表中注册
jti - 用环境变量或 Secrets Manager 管理秘密,严禁提交
- 在客户端将JWT存储在HttpOnly Cookie中而不是localStorage中(XSS防护措施)
用于调试的有用工具
- JWT 解码器:可视化 header / payload / signature,并附带每个 claim 的含义说明
- JWT 生成・签名工具: 使用 Web Crypto API 在浏览器中用 HS256 / HS384 / HS512 进行签名。方便生成测试令牌
- 密码生成工具:可用于生成 secret
总结
JWT 如果使用正确,是一个方便的身份验证令牌,但如果不了解规范陷阱和攻击模式,则会冒着向生产系统引入漏洞的风险。了解本文提出的 6 个威胁和对策,使用最新版本的 JWT 库作为基础。我们建议定期审查您的实现并监控安全公告。