3. JWT 安全风险:Token 泄露、重放攻击、密钥泄露、算法混淆攻击及防御措施
JWT(JSON Web Token)作为无状态认证的核心载体,其安全性直接决定了后端权限矩阵的可靠性。在实际生产环境中,JWT 面临多种攻击向量,包括 Token 泄露、重放攻击、密钥泄露以及算法混淆攻击。本章将系统分析这些风险的技术原理,并给出经过验证的防御策略。
3.1 四大核心风险与攻击原理
下表总结了 JWT 生命周期中最高频的四种安全威胁,以及它们对应的攻击入口和影响范围:
| 风险类型 | 攻击原理 | 攻击入口 | 影响范围 |
|---|---|---|---|
| Token 泄露 | 攻击者通过中间人攻击、XSS、日志泄露等方式获取 JWT 字符串 | 网络传输、浏览器存储、服务端日志 | 攻击者可完全冒充用户身份,访问所有授权资源 |
| 重放攻击 | 攻击者截获合法 JWT 后,在有效期内重复使用该 Token 发起请求 | 网络嗅探、中间人代理 | 非授权操作重复执行,如重复转账、重复下单 |
| 密钥泄露 | 签名密钥(HS256 的对称密钥或 RS256 的私钥)被攻击者获取 | 代码仓库、配置文件、密钥管理服务漏洞 | 攻击者可伪造任意 JWT,彻底瓦解认证体系 |
| 算法混淆攻击 | 攻击者篡改 JWT 头部中的 alg 字段,诱使服务端使用弱算法或无签名验证 |
JWT 解析库的算法信任缺陷 | 绕过签名校验,伪造任意 payload |
关键认知:JWT 本身并不提供加密传输或防重放机制。所有安全属性必须通过传输层(HTTPS)、存储策略(HttpOnly Cookie)和应用层逻辑(nonce、jti)来补充。将安全责任完全交给 JWT 本身是架构设计中的常见误区。
3.2 Token 泄露与防御措施
Token 泄露是最常见的安全事故。JWT 一旦泄露,攻击者即可在 Token 有效期内完全接管用户会话。防御需要从存储、传输和生命周期三个维度入手:
- 传输层强制加密:所有 JWT 必须通过 HTTPS 传输,禁止在 HTTP 明文通道中传递 Token。在微服务间调用时,也应使用 mTLS 或服务网格加密。
- 存储策略选择:优先使用 HttpOnly + Secure + SameSite=Strict 的 Cookie 存储 JWT,避免使用 localStorage 或 sessionStorage,后者易受 XSS 攻击读取。
- 短期 Token + 刷新机制:Access Token 有效期建议控制在 15 分钟以内,配合 Refresh Token 实现无感续期。即使 Access Token 泄露,攻击窗口也被大幅压缩。
- Token 指纹绑定:在 JWT payload 中嵌入客户端指纹(如 User-Agent 哈希、IP 段、TLS 会话 ID),服务端验证时比对指纹,防止 Token 被跨设备使用。
实践建议:对于高安全场景(如金融、医疗),建议引入 Token 绑定(Token Binding)技术,将 JWT 与 TLS 会话强绑定,即使 Token 被复制到其他设备也无法使用。
3.3 重放攻击与防御措施
重放攻击的核心在于攻击者利用 JWT 的“无状态”特性——服务端默认不记录已使用的 Token。防御的关键是引入一次性或时间敏感机制:
| 防御策略 | 实现方式 | 适用场景 | 性能影响 |
|---|---|---|---|
| jti + 黑名单 | JWT 生成时分配唯一 ID(jti),服务端维护已使用 jti 的缓存黑名单 | 关键写操作(转账、删除) | 中等(需缓存查询) |
| nonce 挑战 | 服务端下发一次性 nonce,客户端签名到 JWT 中 | API 网关、敏感接口 | 较高(需存储 nonce) |
| 时间窗口校验 | 严格校验 iat 和 exp,拒绝过期 Token;结合 nbf 防止未来 Token |
所有接口 | 极低(仅时间比较) |
| 请求签名(HMAC) | 将请求体、时间戳、路径等参数签名后放入 JWT 或请求头 | 幂等性要求高的接口 | 中等(需验签计算) |
以下 SVG 图展示了重放攻击的完整链路以及 jti 黑名单机制的防御原理:
3.4 密钥泄露与防御措施
密钥泄露是 JWT 体系中最致命的威胁。一旦签名密钥暴露,攻击者可以伪造任意身份和权限。防御策略需要覆盖密钥的生成、存储、轮换和验证全生命周期:
- 非对称算法优先:使用 RS256 或 ES256 代替 HS256。HS256 是对称算法,签名和验证使用同一个密钥,泄露风险更高;RS256 使用私钥签名、公钥验证,公钥可以安全分发。
- 密钥管理服务(KMS):将密钥存储在专用的密钥管理服务(如 AWS KMS、HashiCorp Vault)中,避免硬编码在配置文件或代码仓库中。应用运行时通过 API 获取密钥,不直接接触密钥明文。
- 定期轮换密钥:建立密钥轮换机制,建议每 90 天轮换一次。轮换时保留旧密钥的验证能力(通过
kid字段标识),直到所有使用旧密钥签发的 Token 过期。 - 密钥泄露应急响应:准备密钥吊销列表(Key Revocation List),一旦发现泄露,立即将泄露密钥的
kid加入黑名单,并强制所有用户重新登录。
架构级建议:在微服务架构中,建议将 JWT 签名和验证职责集中在 API 网关或认证服务中,下游服务只信任网关传递的已解析用户信息(如 user_id、roles),不直接接触 JWT 签名逻辑。这样可以大幅缩小密钥的使用范围。
3.5 算法混淆攻击与防御措施
算法混淆攻击(Algorithm Confusion Attack)利用了 JWT 解析库对 alg 头部字段的信任缺陷。攻击者将 alg 从 RS256 改为 HS256,并使用已知的公钥作为 HMAC 的密钥来签名 Token。如果服务端未严格校验算法类型,就会使用公钥作为 HS256 的密钥进行验证,从而通过伪造的 Token。
防御算法混淆攻击的核心措施:
| 防御措施 | 实现方式 | 代码示例(Java / jjwt) |
|---|---|---|
| 白名单算法校验 | 在 JWT 解析器中显式指定允许的算法列表,拒绝未注册的算法 | |
| 密钥类型绑定 | 根据算法类型选择对应的密钥对象,禁止将 RSA 公钥用于 HMAC 验证 | |
| 禁用 none 算法 | 显式禁止 alg: "none",拒绝无签名 Token |
|
以下 SVG 图对比了正常验证流程与算法混淆攻击的流程差异:
3.6 综合防御矩阵
将上述四种风险的防御措施整合为一个统一的防御矩阵,便于架构设计时对照检查:
| 风险类型 | 传输层防御 | 应用层防御 | 存储层防御 | 监控与应急 |
|---|---|---|---|---|
| Token 泄露 | HTTPS / mTLS | 短期 Token + 指纹绑定 | HttpOnly Cookie | 异常 Token 使用检测 |
| 重放攻击 | TLS 会话复用限制 | jti 黑名单 / nonce | — | 重复 jti 告警 |
| 密钥泄露 | — | 非对称算法 + KMS | 密钥加密存储 | 密钥轮换 + 吊销列表 |
| 算法混淆 | — | 算法白名单 + 密钥类型绑定 | — | 异常算法告警 |
架构落地检查清单:
✅ 所有 JWT 传输是否强制使用 HTTPS?
✅ Access Token 有效期是否 ≤ 15 分钟?
✅ 是否实现了 jti 黑名单或 nonce 机制?
✅ 签名密钥是否存储在 KMS 或 Vault 中?
✅ JWT 解析库是否配置了算法白名单?
✅ 是否禁用了
✅ 所有 JWT 传输是否强制使用 HTTPS?
✅ Access Token 有效期是否 ≤ 15 分钟?
✅ 是否实现了 jti 黑名单或 nonce 机制?
✅ 签名密钥是否存储在 KMS 或 Vault 中?
✅ JWT 解析库是否配置了算法白名单?
✅ 是否禁用了
alg: none?