JWT安全漏洞之密钥混淆攻击(Key Confusion Attack)详解
一、 题目/知识点描述
密钥混淆攻击(Key Confusion Attack),也被称为算法混淆攻击(Algorithm Confusion Attack),是针对JSON Web Token的一种特定安全漏洞。它利用了JWT验证库在处理不同类型密钥(对称加密密钥与非对称加密公钥/私钥)时可能存在的逻辑缺陷。当攻击者能够获取或推测出用于验证JWT签名的公钥时,他们可以构造一个使用对称加密算法(如HS256)签名的恶意Token,但欺骗验证方使用先前获取的非对称加密算法的公钥(如RSA的公钥)作为对称密钥来进行签名验证,从而绕过签名检查,伪造任意内容的JWT。这种攻击在JWT实现不当的应用程序中可能导致身份验证绕过、权限提升等严重后果。
二、 解题过程循序渐进讲解
步骤1:回顾JWT的基本结构与签名机制
首先,我们需要明确JWT通常由三部分组成:头部(Header)、载荷(Payload)和签名(Signature),以点号分隔。
- 头部: 声明令牌类型和签名算法。例如,
{"alg": "HS256", "typ": "JWT"}表示使用HMAC SHA256算法。 - 载荷: 包含声明(Claims),如用户ID、过期时间等。
- 签名: 用于验证消息在传递过程中未被篡改。签名的生成方式取决于
alg字段。- 对于对称算法(如HS256):
签名 = HMAC-SHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), 密钥)。这里的密钥是一个秘密字符串,签名和验证使用同一个密钥。 - 对于非对称算法(如RS256):
签名 = RSA-SHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), 私钥)。验证时使用对应的公钥。
- 对于对称算法(如HS256):
关键在于,验证方必须使用与签名算法alg字段匹配的密钥类型和正确的密钥值来验证签名。
步骤2:理解漏洞产生的根本原因
漏洞的核心在于JWT验证库的实现可能存在缺陷。其验证逻辑可能如下:
- 从JWT头部解码出
alg参数(例如HS256)。 - 根据
alg值,从应用程序配置或密钥库中获取对应的“密钥”。 - 使用这个“密钥”验证签名。
问题出现在第2步和第3步:
- 密钥选择逻辑缺陷: 某些旧的或配置不当的库,可能只有一个“验证密钥”的配置项。当应用最初设计使用RS256时,这个配置项里放的是RSA公钥。但当JWT头部的
alg被篡改为HS256时,验证逻辑可能错误地将这个RSA公钥直接用作HMAC算法的密钥。 - 密码学原理差异: RSA的公钥/私钥对与HMAC的共享密钥本质不同,但它们在代码中可能都以字节序列或字符串形式存在。一个有缺陷的库在收到
alg=HS256的JWT时,不会判断传入的“密钥”是否真的是一个合适的HMAC密钥,而是机械地执行HMAC.verify(token, key),其中key就是那个RSA公钥。如果HMAC验证函数内部只是简单地将传入的key作为二进制密钥使用,而RSA公钥本身就是一个合法的字节序列,那么验证过程在语法上可以运行。
步骤3:攻击者利用条件分析
要成功实施密钥混淆攻击,攻击者需要满足几个条件:
- 获取用于验证的非对称公钥: 这是攻击的前提。公钥有时会硬编码在客户端代码(如JavaScript)、公开的API端点(如
/.well-known/jwks.json)或配置文件中。如果公钥可被预测(如使用默认证书),也可能被获取。 - 目标应用使用有漏洞的JWT库: 应用程序使用的JWT库在处理算法和密钥匹配时存在逻辑缺陷。历史上,一些流行库的旧版本存在此问题。
- 应用允许或受骗使用
alg: none之外的算法: 虽然alg: none攻击更简单,但已被广泛防御。密钥混淆攻击利用了从非对称算法“降级”到对称算法的过程。
步骤4:攻击步骤拆解
假设攻击场景:一个Web应用使用RS256算法签发JWT,其验证公钥(PUBLIC_KEY)是公开的或可被攻击者获取。
- 信息收集: 攻击者首先获取一个有效的JWT(例如通过正常登录),并从中得知应用使用的原始算法(RS256)。更重要的是,他需要通过源代码分析、网络请求探查等方式,获取到用于验证的RSA公钥(PUBLIC_KEY)。
- 构造恶意Token:
a. 篡改头部: 将alg字段从RS256修改为HS256。
b. 篡改载荷: 修改Payload中的声明,例如将"user": "victim"改为"user": "admin","isAdmin": true。
c. 生成签名: 这是关键一步。攻击者将PUBLIC_KEY(RSA公钥)当作HMAC-SHA256的密钥,对新的头部和载荷计算HMAC签名。即:malicious_signature = HMAC-SHA256(base64UrlEncode(modified_header) + "." + base64UrlEncode(modified_payload), PUBLIC_KEY)。
d. 组装Token: 将新的头部、载荷和生成的签名用点号连接,形成恶意JWT。 - 发送与验证: 攻击者将伪造的JWT放入HTTP请求的
Authorization头部,发送给目标应用。 - 触发漏洞: 有漏洞的服务端JWT验证库接收到Token。
- 它解码头部,看到
alg: HS256。 - 它本应去获取HMAC的密钥,但由于配置或逻辑缺陷,它错误地取出了为RS256配置的RSA公钥(PUBLIC_KEY)作为“验证密钥”。
- 它使用
HS256算法,并以PUBLIC_KEY作为密钥,对接收到的头部和载荷计算HMAC值,并与Token中的签名部分进行比较。 - 因为攻击者生成签名时使用的“密钥”(PUBLIC_KEY)和服务端用于验证的“密钥”(同一个PUBLIC_KEY)完全相同,所以HMAC验证通过。服务端错误地认为这是一个由可信方用HS256签发的有效Token,从而接受了被篡改的载荷(如提升为管理员权限)。
- 它解码头部,看到
步骤5:防御措施详解
- 强制指定预期算法: 在验证JWT时,不要在代码中依赖Token头部自声明的
alg值。应该在服务端验证逻辑中,显式指定预期使用的算法。例如,在使用jsonwebtoken库时,应使用jwt.verify(token, PUBLIC_KEY, { algorithms: ['RS256'] }),这样即使Token头部声明alg: HS256,库也会因为不在允许的算法列表中而拒绝验证。 - 密钥与算法匹配检查: 使用最新版本的、维护良好的JWT库。这些库内部会检查提供的密钥类型是否与
alg参数声明的算法匹配(例如,为RS256提供公钥/私钥,为HS256提供足够长度和强度的对称密钥),从而防止密钥混淆。 - 密钥管理: 为不同的算法使用完全不同且无关联的密钥。避免公钥/私钥对中的公钥能作为有效的HMAC密钥字符串使用(虽然这更多是库的职责)。对于对称加密,确保使用强随机生成的、足够长的密钥。
- 最小化公钥暴露: 虽然公钥本身是用于公开验证的,但应避免将其存储在易被恶意利用的上下文(如客户端可直接计算的环境)。但这并非根本解决方法,根本方法在于第一条。
- 依赖库升级与安全审计: 定期更新JWT相关库到安全版本。检查代码中JWT验证的部分,确保没有易受攻击的模式。
总结:密钥混淆攻击是一种利用JWT验证逻辑缺陷,通过算法“降级”和密钥误用,实现签名伪造的攻击。其成功依赖公钥的获取和有漏洞的库。最有效的防御手段是在服务端验证时显式、强制地指定所接受的签名算法列表,绝不信任客户端Token中自带的alg声明。