🔐 JWT 算法混淆攻击:当你的服务器把公钥当成对称密钥来用
JWT(JSON Web Token)是现代 Web 认证的标配。一个 JWT 由三段组成:Header、Payload、Signature,用点号拼接。Header 里有个关键字段叫
典型的混淆场景是这样:服务端用 RS256(非对称,私钥签名、公钥验签)签发 token。攻击者拿到一个合法 token 后,把 Header 的
这个漏洞的本质是一个信任边界错位。RSA 公钥是设计用来公开分发的,任何人都能拿到。但 HS256 要求签名和验签共享同一个秘密。当服务端的验证代码把"公钥"喂进 HMAC 验签函数时,公钥就从"谁能拿到都无所谓"变成了"谁能拿到谁就能伪造 token"。这不是 RSA 或 HMAC 自身的问题,是上层代码没有约束算法选择造成的。
现实中防御的核心只有一条:服务端必须在验证之前,把
🧪 你的服务用 RS256 签发 JWT,公钥在
💡 核心记忆:安全验证永远不信任客户端声明的算法——
#Security
JWT(JSON Web Token)是现代 Web 认证的标配。一个 JWT 由三段组成:Header、Payload、Signature,用点号拼接。Header 里有个关键字段叫
alg,声明这个 token 用什么算法签的名。服务器验证时,本应严格按 Header 里声明的 alg 去执行对应的验签逻辑。问题就出在这里——很多库的实现没有在验签前把 alg 绑定到服务端预期的算法上,而是直接信任客户端传来的 Header。典型的混淆场景是这样:服务端用 RS256(非对称,私钥签名、公钥验签)签发 token。攻击者拿到一个合法 token 后,把 Header 的
alg 改成 HS256(对称,同一个密钥既签名又验签),然后用服务端公开的 RSA 公钥作为 HMAC 密钥重新签名整个 token。服务端收到后看到 alg 是 HS256,就走进 HMAC 验签分支,拿"密钥"去算 HMAC——而这个"密钥"恰恰就是攻击者手上有的那把 RSA 公钥。验签通过,攻击者以任意身份通过认证。这个漏洞的本质是一个信任边界错位。RSA 公钥是设计用来公开分发的,任何人都能拿到。但 HS256 要求签名和验签共享同一个秘密。当服务端的验证代码把"公钥"喂进 HMAC 验签函数时,公钥就从"谁能拿到都无所谓"变成了"谁能拿到谁就能伪造 token"。这不是 RSA 或 HMAC 自身的问题,是上层代码没有约束算法选择造成的。
现实中防御的核心只有一条:服务端必须在验证之前,把
alg 写死成一个预期值,不信任 token Header 里客户端声明的算法。大多数现代 JWT 库已经修复了这条路径,比如 Node 的 jsonwebtoken 在 jwt.verify() 时要求显式传入 asymmetricPublicKey 或 secret,库内部会拒绝用公钥做 HMAC。但如果你用的是老旧库版本,或者自己拼了验证逻辑,这条攻击路径依然敞开。另外,如果服务端含有 alg: none 的回退逻辑——即允许无签名的 token 通过——那是另一条同样危险的路径,根本原因一样:信任了客户端对自己如何被认证的自述。🧪 你的服务用 RS256 签发 JWT,公钥在
/.well-known/jwks.json 公开。攻击者把 alg 从 RS256 改成 HS256,用拿到的公钥做了 HMAC 签名,服务端居然验证通过了。最小改动应该改哪里?algorithms: ['RS256']alg💡 核心记忆:安全验证永远不信任客户端声明的算法——
alg 字段是别人说的话,只有服务端写死的算法才是你定的规矩。#Security