JWT安全漏洞与防护
字数 2627 2025-11-06 12:41:12
JWT安全漏洞与防护
描述:
JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间安全地传输信息作为JSON对象。它通常用于身份验证和授权。JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature),格式为Header.Payload.Signature。尽管JWT设计是安全的,但如果使用或配置不当,会引入严重的安全漏洞,导致身份验证被绕过或用户账户被劫持。
解题过程:
第一步:理解JWT的结构与验证机制
-
结构分解:
- 头部(Header):通常包含两部分,令牌类型(即"JWT")和所使用的签名算法(如HMAC SHA256或RSA)。例如:
{"alg": "HS256", "typ": "JWT"}。这个JSON对象会被Base64Url编码形成JWT的第一部分。 - 载荷(Payload):包含声明(Claims)。声明是关于实体(通常是用户)和其他元数据的语句。常见的声明有
iss(签发者)、exp(过期时间)、sub(主题)等。也可以包含自定义声明,如username。同样,这个JSON对象会被Base64Url编码形成JWT的第二部分。 - 签名(Signature):用于验证消息在传输过程中没有被篡改。签名的生成方式依赖于头部中指定的算法。例如,对于HMAC SHA256算法:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)。这个签名是JWT的第三部分。
- 头部(Header):通常包含两部分,令牌类型(即"JWT")和所使用的签名算法(如HMAC SHA256或RSA)。例如:
-
验证流程:
服务器在收到JWT后,会:
a. 将收到的JWT按.分割成三部分。
b. 使用相同的密钥(对于对称加密如HS256)或公钥(对于非对称加密如RS256)重新计算签名。
c. 将计算出的签名与JWT中附带的签名(第三部分)进行比对。
d. 同时,验证载荷中的声明,如过期时间(exp)是否有效。
第二步:识别常见的JWT安全漏洞
-
算法混淆攻击(Algorithm Confusion Attack):
- 漏洞成因:JWT支持多种签名算法。最危险的情况是,服务器代码在验证签名时,依赖JWT头部中的
alg参数来决定使用何种算法进行验证。如果攻击者将alg改为none(如果服务器配置允许),或者从非对称算法(如RS256,使用私钥签名、公钥验证)改为对称算法(如HS256,使用同一个密钥进行签名和验证),而服务器未正确区分处理,就会导致漏洞。 - 攻击过程(以RS256改为HS256为例):
- 攻击者截获一个使用RS256算法签名的合法JWT。
- 他将头部中的
alg改为HS256。 - 他修改载荷(例如,将用户名改为
admin)。 - 由于服务器配置错误,它看到
alg: HS256,就会使用HMAC算法和公钥(本应用于验证RS256签名)作为密钥来验证签名。攻击者由于也能获得公钥(公钥通常是公开的),所以他可以用公钥作为HMAC的密钥,生成一个有效的签名。 - 服务器用公钥作为HMAC密钥验证签名,结果匹配,误认为Token合法。
- 防护:服务器在验证JWT时,必须强制指定期望的签名算法,而不是依赖Token头中的
alg值。例如,代码应写死只使用RS256进行验证。
- 漏洞成因:JWT支持多种签名算法。最危险的情况是,服务器代码在验证签名时,依赖JWT头部中的
-
弱密钥(Weak Secret):
- 漏洞成因:当使用HMAC(对称加密)算法时,签名和验证使用同一个密钥。如果这个密钥强度很弱(如
secret、password等常见字符串),或者密钥是公开的、容易猜解的,攻击者就可以暴力破解密钥,然后伪造任意Token。 - 防护:使用足够长、足够复杂且不可预测的密钥(推荐至少32字节的随机字符串)。绝对不要使用弱密码或公开的信息作为密钥。
- 漏洞成因:当使用HMAC(对称加密)算法时,签名和验证使用同一个密钥。如果这个密钥强度很弱(如
-
未验证签名(None Algorithm Attack):
- 漏洞成因:早期的一些JWT库支持
alg: none算法,表示无签名。如果服务器没有明确禁用这种算法,攻击者可以篡改头部和载荷后,将签名部分置空,从而绕过签名验证。 - 防护:在现代JWT库中,通常已默认禁用
none算法。但在使用时,仍需在代码层面明确指定允许的算法列表,并排除none。
- 漏洞成因:早期的一些JWT库支持
-
令牌泄露(Token Leakage):
- 漏洞成因:JWT本身包含用户身份信息。如果Token通过不安全的通道传输(如HTTP明文),或在前端存储不当(如存在LocalStorage易受XSS攻击读取),会导致令牌被窃取。攻击者获得Token后,在有效期内可以冒充用户。
- 防护:
- 始终使用HTTPS传输JWT。
- 考虑将JWT存储在
HttpOnly的Cookie中,可以缓解XSS攻击导致的令牌窃取(但需注意CSRF防护)。 - 设置较短的过期时间(
exp),并使用刷新令牌(Refresh Token)机制来获取新的访问令牌。
-
无效的声明验证:
- 漏洞成因:服务器虽然验证了签名,但没有验证载荷中的关键声明。例如,没有检查
exp(过期时间)或nbf(不早于时间),导致过期的Token依然有效。 - 防护:在验证逻辑中,必须对
exp、nbf、iss(签发者)等声明进行强制性验证。
- 漏洞成因:服务器虽然验证了签名,但没有验证载荷中的关键声明。例如,没有检查
第三步:安全开发与配置最佳实践总结
- 算法选择:优先使用非对称算法(如RS256),私钥由授权服务器安全保存,公钥分发给资源服务器进行验证,这样更安全。
- 强制指定算法:在验证JWT的代码中,显式声明允许的算法,如
[RS256],不信任客户端提供的alg头。 - 强密钥管理:如果使用对称算法(HS256),密钥必须高强度且严格保密。密钥应像密码一样被安全地存储和管理,定期轮换。
- 全面声明验证:不仅要验证签名,还要验证所有必要的声明(
exp,nbf,iss,aud等)。 - 安全的令牌传输与存储:全程使用HTTPS。谨慎选择客户端存储方式(
HttpOnlyCookie + CSRF防护是一种较安全的选择)。 - 使用成熟的库:使用经过社区广泛验证的、最新的JWT库,避免自己实现编码/解码和验证逻辑。
- 最小化原则:不要在JWT的载荷中存放敏感信息(如密码),因为载荷只是Base64编码,并非加密(除非使用了JWE进行整体加密)。