关于JWT算法混淆攻击的实现及jwcrypto库防护逻辑的疑问
咱们先把你的问题拆成两部分理清楚:为什么你的jwcrypto代码会报错,以及你对算法混淆漏洞核心的理解是否正确——先给结论:你的理解完全正确,而jwcrypto的报错正是它安全设计的体现。
首先,JWT算法混淆漏洞的核心前提
这个漏洞能成功,本质上是服务器端的验证逻辑犯了一个致命错误:把JWT头里的alg字段当成了选择验证密钥和验证方式的唯一依据,并且把非对称加密场景下的公钥,直接当成了对称加密(HMAC)的共享密钥来使用。
正常的安全逻辑应该是:不管JWT头写的alg是什么,对应RSA密钥对的JWT就必须用RSA公钥验证;对应HMAC的JWT就必须用预设的对称密钥验证。但有漏洞的逻辑会是:
- 先读取JWT头的
alg字段 - 根据
alg来决定怎么处理密钥字符串——比如,原本是RSA公钥的PEM/JWK字符串,当alg是HS256时,就被直接当成HMAC的密钥字节流来用,而不是先解析成非对称密钥对象
为什么你的jwcrypto代码会报错?
jwcrypto的设计思路是强类型的密钥对象绑定,它从API层面就把密钥的类型和算法的使用场景锁死了:
- 你加载的是只有公钥部分的EC JWK对象,这个对象的属性只包含非对称公钥的材料,没有HMAC签名需要的对称密钥材料
- 当你尝试用这个公钥对象+HS256算法调用
make_signed_token时,库会直接检查密钥是否符合算法的要求——HMAC需要的是对称密钥(字节流或包含对称密钥材料的JWK),而你给的是非对称公钥,所以直接抛出错误
这个报错不是bug,恰恰是jwcrypto在帮你避开算法混淆的风险:它不会允许你用一个非对称密钥的公钥部分去做对称签名,更不会根据JWT头的alg字段动态改变密钥的使用方式。
你的理解完全正确:漏洞和“密钥的字符串化动态处理”强相关
你说的“算法混淆只能发生在密钥以序列化字符串形式传递,且根据JWT的alg字段反序列化/使用密钥的场景”,这个结论100%正确。
举个典型的有漏洞的伪代码(比如用一些封装不完善的库或者手写的验证逻辑):
# 有漏洞的验证逻辑示例(伪代码) def vulnerable_jwt_verify(token): # 解析JWT头 header = jwt_get_header(token) # 从JWKS接口拿到公钥的字符串(比如PEM格式) public_key_str = fetch_public_key_from_jwks() if header["alg"] == "RS256": # 正常的RSA验证:把字符串解析成RSA公钥对象 rsa_key = load_rsa_public_key(public_key_str) return rsa_verify_signature(token, rsa_key) elif header["alg"] == "HS256": # 致命错误:把RSA公钥字符串直接当HMAC的密钥用 return hmac_verify_signature(token, public_key_str.encode())
这种逻辑就会被攻击者利用:把JWT的alg改成HS256,用RSA公钥字符串当HMAC密钥签名,服务器收到后会用同样的公钥字符串做HMAC验证,最终通过。
而jwcrypto的设计从根源上避免了这种情况:你必须先把密钥解析成对应的强类型对象(RSA公钥、HMAC密钥等),然后在验证时,算法和密钥类型是匹配的——不会出现“把RSA公钥当HMAC密钥用”的情况。
怎么写单元测试模拟这个漏洞?
如果你要测试自己的实现有没有这个漏洞,不能用jwcrypto的安全API,而是要模拟那种“密钥以字符串存储,根据alg动态处理”的有漏洞逻辑——比如手动解析JWT头,然后根据alg选择用公钥字符串做HMAC验证,再看是否能通过攻击者构造的恶意JWT。
内容来源于stack exchange




