🔐 JWT Algorithm Confusion:为什么“验签”也会被你自己绕过

很多人第一次学 JWT,会把注意力放在“有没有签名”上,却忽略了更关键的一件事:服务端到底是按什么规则验这个签名的。JWT 头里有一个 alg 字段,告诉接收方“我用的是哪种算法”。问题就出在这里——如果服务端傻乎乎地信了这个字段,让令牌自己决定该怎么被验证,攻击者就有机会把“本该用非对称密钥验证的令牌”,伪装成“用对称密钥验证的令牌”,甚至在更糟的实现里直接改成 none。这不是密码学失效,而是实现者把控制权交给了不可信输入。

现实里最典型的坑,是服务端原本想用 RS256。正常情况下,签发方拿私钥签名,验证方拿公钥验证,攻击者只有公钥也没法伪造。但如果验证库或业务代码写得烂,看到 JWT 头里写着 HS256 就切到 HMAC 模式,而且还把“本来公开的 RSA 公钥”当成 HMAC 的 secret 去验,那攻击者就能用这个公开公钥自己生成一个“合法”签名。于是整个认证系统从“只有私钥持有者能签”退化成“谁拿到公钥谁都能签”。这就是 Algorithm Confusion 的本质:不是数学被攻破,是程序把不同信任模型混在了一起。

为什么这种事会发生?因为很多系统把“解析 token”和“决定安全策略”塞进了一步,开发者图省事,直接把 token 丢进库里让它自动判断。自动判断在业务代码里通常就是灾难。安全边界不该由攻击者上传的头字段决定,应该由服务端配置决定:这个接口只接受 RS256,那就硬编码只验 RS256;这个 issuer 的 key set 是什么,也应该提前绑定,而不是收到什么就信什么。再往前一步,kid 这种字段如果还能触发本地文件读取、远程取 key、或从多个 key 中随便选,那问题只会更大。

防御其实不复杂,但很多团队就是不做。第一,服务端必须显式指定允许的算法,不能从 token 头里“协商”。第二,不要把同一套验证逻辑同时兼容 HMAC 和 RSA/EC,混用就是在制造特殊情况。第三,验证时把 issuer、audience、过期时间、not before 一起校验,不要只看签名过不过。第四,选库时看默认行为,凡是“自动推断算法”“自动接受 none”“随手喂一个 key 就能跑”的库,都该提高警惕。安全问题最烦的地方就在这:代码看起来能跑,用户也能登录,但边界已经烂了。

🧪 你在审一个老系统,发现它验证 JWT 的代码会先读取 token 头里的 alg,如果是 RS256 就用公钥验签,如果是 HS256 就把同一份配置里的 publicKey 字符串当作 HMAC secret。现在攻击者只能拿到公开公钥,不能拿到私钥。这个系统还能被伪造管理员 token 吗?为什么?答案:algHS256

💡 核心记忆:JWT 的算法选择权必须在服务端手里,绝不能交给 token 头自己决定。

#Security