签名的好坏取决于验证器
一个 的签名使其声明可信,但前提是验证它的代码正确地这样做。大多数 JWT 漏洞并非密码学中的缺陷;它们是可被诱使接受本应拒绝令牌的验证器。以下是重要的那些,以及防止它们的检查。
信任令牌自身的算法
alg: none。 JWT 规范定义了一个无签名的“未受保护”令牌,以{"alg":"none"}声明。一个遵从它的验证器将接受一个带有伪造声明和空签名的令牌。一个正确的验证器绝不会把none视为对一个本应被签名令牌的有效值。- 算法混淆( 变 )。 在 RS256 中,令牌由一个私钥签名并用公钥验证。如果一个验证器从令牌读取算法,而攻击者把头部改为 HS256,验证器可能尝试用公钥作为密钥来验证一个 ,而这个值攻击者也持有。伪造的令牌于是通过。
两者唯一的防御相同:由验证器决定可接受的算法,而非令牌。 在你的验证调用中固定预期的算法(或一个简短的允许列表),并拒绝其他任何算法。
跳过声明
一个有效签名证明令牌已被签发且未被篡改。它并不证明该令牌是给你的或仍然有效。一个正确的验证器还会检查:
exp(过期) 和nbf(不早于):拒绝已过期或尚未生效的令牌,并允许一个小的时钟漂移容差。aud(受众):确认该令牌是为此服务而设的。为服务 A 签发的令牌不应被服务 B 接受。iss(签发者):确认它来自你信任的签发者,并从该签发者而非从令牌中推导验证密钥。
省略这些,正是一个被盗或误投的令牌最终在其不应被接受之处被接受的方式。
较小的陷阱
- 弱 HMAC 密钥。 一个用简短或可猜测密钥签名的 HS256 令牌,可被离线暴力破解直到找到一个匹配的签名。请使用一个长的随机密钥。
- 处理
kid与 。kid头部选择一个密钥;把它视为不受信任的输入(它曾被用于注入),且只从你信任的签发者的 JWKS 获取密钥。 - 把解码与验证混为一谈。 在不检查签名和时间的情况下读取一个令牌的声明并非验证。只对一个完全验证过的令牌采取行动。
简短版
固定算法、验证 exp、aud 与 iss、使用强密钥,并且绝不把解码与验证混为一谈。JWT 工具 会解码一个令牌,以清晰的语言显示其声明与过期,并对你粘贴的一个密钥验证一个 HMAC 签名,全部在你的浏览器中,令牌和密钥绝不会被发送。