JWT 解码工具

读取令牌的头部与声明,看清它对自身的描述。只解码,不验签。

解码不等于验证。 JWT 中可读的两段只是 base64 —— 谁都能读,谁也都能写。让令牌值得信任的只有签名,而验证签名需要密钥。本页没有密钥,也不会向您索要,并且对令牌是否真实不作任何判断。

开头的「Bearer」会被自动去掉。您粘贴的任何内容都不会离开本页。

三段,其中只有一段是要紧的

一个 JWT 是三段用点连起来的 base64url 字符串。前两段是你能读懂的 JSON;第三段是对前两段的签名。

eyJhbGciOiJIUzI1NiJ9  .  eyJzdWIiOiIxMjM0In0  .  4fXK...
      header                    payload            signature

头部说明是哪个算法签的名。载荷携带各项声明。两者都没有加密——base64 是传输编码,不是遮掩,任何持有该令牌的人大约一秒钟就能把两段都读出来。

只有签名在真正做安全工作。它证明前两段是由持有签名密钥的人产出的,而且此后未被修改。不去核对它,一个 JWT 就只是一份自述的文本文件,权威性不比客户端发给你的任何其他字符串高。

这也是为什么本页不会告诉你某个令牌是有效的

验证需要密钥,而「把你的签名密钥粘进一个网页」这句话,没有任何一个版本称得上是好建议。本工具改为报告这个令牌关于它自身的说法:它是否已过期,它的生效时间是否已到,算法是对称的还是非对称的,以及它是否宣称自己压根不需要签名。

alg:none,以及它为什么很有启发

规范允许 "alg": "none"——一个没有签名的令牌。攻击手法自己就写出来了:拿一个合法的令牌,把载荷改成你想声称的任何内容,把算法设成 none,删掉签名,发出去。

一个从头部读出算法、然后照着做的库,会接受它。是攻击者选定了验证策略,因为那条策略就写在报文里由攻击者控制的那一部分。

与之相关的另一个版本是算法混淆。一台用 RS256 做验证的服务器持有公钥,而公钥并不保密。把头部改成 HS256——一个对称算法——然后拿那把公钥当共享密钥去签名。一个天真的库会对两者使用同一份密钥材料,于是伪造的令牌通过了验证。

两者的修复方式相同,而且这条通用原则值得带到别处去:由验证方声明它接受哪些算法,而不是去问令牌。

生产环境里实际会出什么问题

症状常见原因
刚签发就被拒绝时钟偏差。iatnbf 比验证方的时钟快了几秒。留一点小容差。
本地能用,部署上去就不行各环境的签名密钥不同——把测试环境的令牌发给了生产环境。
签名有效,还是被拒绝audiss 声明与那个服务期待的不一致。
一开始能用,会话中途就不行了过期。短寿命的令牌需要一套刷新流程,而缺的通常就是这一块。
请求因为头部太大而失败令牌长过了限制。多数服务器把响应头封在 8 KB 上下,而浏览器给单个 cookie 的上限是 4 KB。

吊销问题

使用 JWT 的理由,正是验证不需要查数据库——令牌自带它的证明。而这恰恰也是你无法吊销它的原因。验证时什么都不会被查阅,所以没有任何地方可以记下「这个令牌该停止生效了」。

可选方案,没有一个是免费的:

  • 很短的过期时间加上刷新令牌。 标准答案。访问令牌只活几分钟,刷新令牌则拿去数据库核对,并且可以被吊销。
  • 一份黑名单。 拿每个令牌的 jti 去一份已吊销清单里核对——这就把这种格式当初为了避开而存在的那次查询又请了回来。
  • 轮换签名密钥。 一次性让所有令牌失效,包括其他所有人的。

如果你对「立刻吊销单个会话」的需求,大过你对「无状态验证」的需求,那么用一个由存储支撑的不透明会话标识符是更简单的设计,而它被低估了。

相关

三段全都是URL 安全的 Base64,且不带填充——这就是为什么一段内容放进严格的解码器里常常失败。解码之后,那两段可读的部分是 JSON。而对于这个令牌所代表的那份凭据,强度检测器覆盖的是登录的另一端。

JWT 相关问题

解码一个 JWT 就等于验证了它吗?

不等于,而这个区别就是整个安全模型所在。头部和载荷是 base64url 编码的——读它们不需要任何东西。验证的意思是用密钥重新计算签名并作比对,这才证明令牌确实由持有该密钥的一方签发,且此后未被改动。一个没有密钥的解码器只能告诉你令牌宣称了什么;只有验证才能告诉你该不该相信它。

把真实令牌粘进一个网站安全吗?

粘进这个页面是安全的——解码在你的浏览器里进行,没有任何内容被传输。但提出这个问题背后的直觉是好的,值得一直保持。JWT 通常就是一份有效的凭据:在它过期之前,任何持有它的人都能以你的身份行事。一个把它 POST 到服务器的解码器,等于被交到手里一个能用的会话。在把生产环境的令牌粘进任何工具之前,先弄清楚它在哪里干活。

我可以把机密放进 JWT 载荷里吗?

不可以。载荷是被签名的,不是被加密的——任何持有该令牌的人都能读,包括令牌签发给的那个用户。签名保证内容没有被改动,但对隐藏内容毫无作用。如果你确实需要一个保密的载荷,那属于 JWE,那是另一套复杂得多的规范。实践中更好的答案通常是:把敏感的部分留在服务器上,令牌里只放一个标识符。

什么是 alg:none 漏洞?

JWT 规范允许 "alg" 取值为 "none",也就是一个未签名的令牌。攻击手法是:拿一个有效令牌,把载荷改成声称自己是管理员,把 alg 设为 none,再把签名删掉。一个信任头部里算法字段的库会判定它有效——因为令牌自己声明了不需要检查。所有主流库都已经打过补丁,但这个教训是可以推广的:该由服务器决定接受哪些算法,而不是让令牌自己提名一个。

怎样在 JWT 过期之前吊销它?

基本上做不到,而这正是这种格式所做的取舍。JWT 不需要查数据库就能验证,这正是它快的原因——同时也正因如此,验证时被查阅的东西里,没有一样知道你想吊销它。可行的办法是:设很短的过期时间并配合刷新令牌;维护一份被吊销标识符的黑名单并在每次请求时检查(这就把你本想避开的那次查询又请了回来);或者轮换签名密钥,一次性让所有令牌失效。

令牌看着没问题,为什么被拒绝了?

除了过期之外,常见原因是签发方和验证方之间的时钟偏差;受众(audience)或签发者(issuer)声明与服务器期望的不一致;或者令牌是用另一把密钥签的,而不是验证方持有的那把——最常见的就是拿测试环境的令牌去打生产环境。上面的解码器会标出它能看见的时间类问题;受众不符和密钥不符,只有服务器才能告诉你。

最后审校于 。发现有内容过时了? 告诉我们.