🔐 JWT 算法混淆:为什么“验签”也会被你自己绕过

很多人第一次接触 JWT(JSON Web Token)时,会把它理解成“服务端签个名,客户端带回来,后端再验一下”。问题就出在这个“验一下”常常不是在验安全边界,而是在验一段可被攻击者影响的元数据。JWT 头里有一个 alg 字段,告诉服务端“我是用什么算法签的”。如果你的代码把这个字段当成可信输入,攻击者就能试着把原本该用非对称签名的 token,伪装成对称签名的 token,让服务端拿本该公开的公钥去当作 HMAC 密钥验证,结果就是:明明没有私钥,也能伪造出看起来合法的身份令牌。

这事为什么会发生?因为开发者把“算法选择权”交给了 token 自己。系统原本设计是:服务端预先知道应该接受 RS256 还是 ES256,验签逻辑按固定规则走。但一旦实现变成“先读 token 头里的 alg,再决定怎么验”,安全边界就倒了。攻击者最喜欢的就是这种“由输入决定防御方式”的代码,因为它表面上看很灵活,实际上是在让敌人选你用哪把锁。

现实里它最容易出现在两类地方。第一类是直接调用 JWT 库的默认解析函数,没有显式限制允许的算法集合。第二类是多语言微服务里,一个服务负责签发,另一个服务只会“通用校验”,结果把 RSA 公钥、HMAC 密钥、甚至 JWK 拉取逻辑混在一起。代码能跑,不代表边界是对的。安全协议里最危险的 bug,往往不是密码学算法弱,而是工程实现把“谁说了算”搞反了。

防这类问题不靠口号,靠死规则。服务端必须自己固定可接受的算法,不从 token 里“学习”算法;对称密钥和非对称密钥的校验路径必须彻底分开,别写一个万能 verify(token, key) 以为很优雅;kidjkux5u 这类会影响取钥方式的字段也不能随便信,否则你修完算法混淆,转头又把密钥来源交给了攻击者。说穿了,这不是数学问题,是边界控制问题:令牌内容可以被看见,但不能决定验证策略。

🧪 你接手了一个旧系统,登录服务签发的是 RS256 的 JWT,资源服务在校验时直接读取 token 头里的 alg,然后调用同一个 verify(),传入的 key 是公开的 RSA 公钥。现在有人说“既然没有私钥,伪造 token 不可能”。这句话错在哪?
alg

💡 核心记忆:JWT 的 alg 可以被看见,但绝不能由它来决定你怎么验签,算法和密钥类型必须由服务端预先固定。

#Security