本文目录导读:

- 修复算法混淆攻击(最核心的漏洞)
- 别把敏感信息放在Payload里(且未加密)
- 修复密钥泄露或弱密钥
- 加强Token存储安全(防止XSS/CSRF导致泄露)
- 完善Token生命周期管理(防重放与劫持)
- 验证关键声明(防止未授权访问)
- 防止JWT被篡改后依然有效
- 防护清单
JWT(JSON Web Token)本身是一种规范,其安全性取决于实现方式和使用场景,要防护和修复JWT相关的漏洞,需要从算法、密钥管理、存储、验证流程等多个维度入手。
以下是针对常见JWT漏洞的防护修复指南:
修复算法混淆攻击(最核心的漏洞)
漏洞原理: 攻击者将JWT头部的alg从RS256(非对称加密)篡改为HS256(对称加密),并使用公钥(通常是公开的)作为密钥进行签名,服务器如果未严格校验算法类型,会使用公钥作为HS256的密钥进行验证,导致攻击成功。
-
修复方案:
-
严格校验算法白名单: 在服务端验证JWT时,必须明确指定允许的算法列表。不要从JWT的Header中动态获取算法。
-
代码示例(Node.js jsonwebtoken库):
// 错误做法:从Header动态获取算法 jwt.verify(token, secretOrPublicKey); // 正确做法:强制指定算法,拒绝非预期算法 jwt.verify(token, publicKey, { algorithms: ['RS256'] }); // 只允许RS256 -
拒绝
none算法: 确保服务器拒绝算法为none的JWT令牌,或明确禁用none算法。
-
别把敏感信息放在Payload里(且未加密)
漏洞原理: JWT的Payload只是Base64编码,不是加密,任何人只要拿到Token,就能解码看到Payload内容(如密码、身份证号)。
- 修复方案:
- 清除敏感数据: JWT中只存放必要且不敏感的信息,例如用户ID(User ID)、角色(Role)、过期时间(exp),敏感数据(密码、信用卡号、手机号)要放在服务端的Session或数据库中。
- 使用JWE(加密): 如果必须传递敏感信息,使用JWE(JSON Web Encryption)对Payload进行加密,而不是仅使用JWS(签名)。
修复密钥泄露或弱密钥
漏洞原理: 使用简单的字符串(如"secret")作为HMAC密钥,或RSA私钥被泄露。
- 修复方案:
- 使用强随机密钥: HMAC密钥长度至少256位(32字节),使用密码学安全的随机生成器生成。
- 定期轮换密钥: 定期更换签名密钥(Key Rotation),旧令牌在到期前仍然有效,但新令牌使用新密钥签发。
- 优先使用非对称算法: 服务端负责签名的私钥要严格保管(可放在环境变量、密钥管理服务KMS或硬件安全模块HSM中),公钥可以安全分发给其他服务。
加强Token存储安全(防止XSS/CSRF导致泄露)
漏洞原理: 开发者将JWT存储在localStorage或sessionStorage中,一旦网站存在XSS漏洞,攻击者可以轻松窃取Token。
- 修复方案:
- 首选方案(加HttpOnly Cookie): 将JWT放在
HttpOnly、Secure、SameSite=Strict的Cookie中,这样可以防止XSS读取Token,并利用SameSite属性防御CSRF。 - 备选方案(Memory/Web Worker): 如果无法使用Cookie(如移动端或纯前端),将Token存储在JavaScript内存变量中(页面刷新会丢失,需要配合刷新令牌机制),或放在Web Worker中。
- 避免方案: 不要将Token直接存在
localStorage或sessionStorage中。
- 首选方案(加HttpOnly Cookie): 将JWT放在
完善Token生命周期管理(防重放与劫持)
漏洞原理: JWT一旦签发,在过期前永久有效(除非服务端有黑名单机制),如果Token被窃取,攻击者可以长期使用。
- 修复方案:
- 设置短时效的Access Token: Access Token过期时间应很短(如15-30分钟)。
- 引入Refresh Token机制: 使用一个长期有效的(如7天)、存储在数据库或白名单中的Refresh Token来换取新的Access Token,Refresh Token被使用一次后应立即作废。
- 服务端黑名单/吊销机制: 如果用户登出或修改密码,将旧的Access Token加入服务端的黑名单(牺牲无状态性换取安全性),或直接修改用户密码哈希值(使旧Token失效)。
验证关键声明(防止未授权访问)
漏洞原理: 服务端只验证签名,不验证aud(受众)、iss(签发者)等声明。
- 修复方案:
- 验证受众(aud): 确保Token是发给当前服务使用的。
- 验证签发者(iss): 确保Token来自受信任的授权服务器。
- 验证过期时间(exp): 默认必须检查,如果Token已过期,拒绝任何操作。
防止JWT被篡改后依然有效
漏洞原理: 极少数库有解析漏洞(如某些库会忽略签名验证)。
- 修复方案:
- 正确使用API: 务必使用
.verify()方法验证签名,而不是.decode()直接解码。.decode()仅解码,不验证签名。
- 正确使用API: 务必使用
防护清单
| 漏洞类型 | 修复动作 | 优先级 |
|---|---|---|
| 算法混淆 | 服务端硬编码算法白名单,禁用none算法 |
最高 |
| 数据泄露 | 清除Payload中的敏感信息,或使用JWE加密 | 高 |
| 密钥泄露 | 使用强随机密钥,启用密钥轮换 | 高 |
| 存储不安全 | 使用HttpOnly Cookie存储Token | 高 |
| 重放攻击 | 设置短Access Token + Refresh Token机制 | 中 |
| 未校验Claims | 验证aud、iss、exp等标准声明 |
中 |
| 不当使用API | 总是使用.verify(),不用.decode() |
高 |
如果你使用的是成熟的框架(如Spring Security、Django REST Framework、ASP.NET Core Identity),建议启用其内置的JWT安全配置,少走弯路。