🔐 JWT 算法混淆:为什么“会验签”也可能被绕过

很多人第一次接触 JWT(JSON Web Token)时,会把它理解成“服务端签了名,客户端带回来,后端验一下就安全了”。问题就在这里:如果验证代码先相信了令牌头里的 alg 字段,再决定用什么方式验签,攻击者就有机会把“安全设计”变成“自己选规则的考试”。这类问题通常叫算法混淆,最经典的是把原本应该用非对称签名的 RS256,骗成用对称签名 HS256。服务端如果错误地把公开的 RSA 公钥当成 HMAC 密钥去验,攻击者就能自己伪造一个“合法”令牌,因为公钥本来就是公开的。更糟的是,早年还有一些库接受 alg=none,等于告诉服务端“这张票没签名,但你也别查了”。

它为什么会发生,不是因为密码学本身玄学,而是因为实现者把“算法选择权”交给了不可信输入。JWT 的 header 是令牌的一部分,本来就可以被用户改,后端却常常写成“你说你是 HS256,那我就按 HS256 验;你说你是 RS256,那我就按 RS256 验”。这就像门卫先问来的人“你想按什么规则进门”,然后真的照做。现实里这种 bug 常出现在统一认证网关、微服务中间件、自己封装的登录组件里,尤其是“支持多种算法、多个 issuer、多个环境”的代码,分支一多,垃圾就冒出来了。

真正的防法很直接:算法不能从 token 里“动态信任”,而要由服务端配置写死。某个 issuer 只允许某一种算法,某把 key 只配某一种用途,验签库也要显式指定允许列表,而不是吃默认值。再往前一步,连 kid(key id)也不能无条件信任,否则又会出现路径穿越、远程取 key、缓存投毒这种后续事故。简单说,token 可以携带“声明”,但不能决定“规则”;规则必须在服务端。

🧪 你在审计一个内部 API 网关,发现它会先解析 JWT header,再根据其中的 alg 决定调用 HMAC 还是 RSA 验签;而且 RSA 公钥能从公开的 JWK endpoint 拿到。现在系统原本使用 RS256。最危险的攻击路径是什么?alg

💡 核心记忆:JWT 的 header 可以看,但绝不能信,验签算法和密钥类型必须由服务端预先绑定,不能让令牌自己决定。
#Security