术语表

JWT(JSON Web Token)是什么?带签名令牌的原理与安全用法

JWT(JSON Web Token)是服务器签名后发放、可发现篡改的令牌。讲解头部、载荷、签名三部分的原理,以及不校验签名、允许 alg:none、放入机密、有效期过长等风险的防御。

发布于 2026-06-12 更新于 2026-10-06 3 分钟阅读
本页目录

登录后由服务器交给你的带签名的令牌,就是 JWT。本文讲解它的原理以及安全使用的关键要点(不会写出攻击手法)。

原理(三个部分)

JWT 是用 . 把 头部.载荷.签名 三部分连接而成的字符串。头部和载荷只是用 base64url 编码(并非加密),只有签名是用密钥(或共享密钥)生成的。

① 头部

签名算法等元信息。任何人都能读

② 载荷

用户 ID、有效期等主张。任何人都能读=不要放机密

③ 签名

用密钥生成。保证未被篡改

JWT 由三部分组成。头部和载荷「只是可读」,真品保证由签名承担。

内容「只是可读」这一点至关重要。所以载荷不是放密码或个人信息的地方。JWT 的价值在于:服务器通过校验签名,就能确认「这个令牌是我签发的、且未被篡改」。

安全使用的防御措施

1

必须校验签名,并固定 alg(最重要)

在服务器端每次都校验签名。把允许接受的签名算法(alg)固定为期望的值,并拒绝 alg:none(无签名)。不要轻信令牌头部里写的 alg。

JWT 最佳实践 RFC(RFC 8725)规定:库必须让调用方能够指定所支持的算法集合,并且不得使用其他算法;同时必须校验 JWT 中的所有密码学运算,只要有一项失败就拒绝整个 JWT。

2

不只拒绝 alg:none,也要阻止算法切换

对 alg 的滥用不只是去掉签名。RFC 8725 描述了这样一种情况:把 RS256 改成 HS256 后,某些库会把 RSA 公钥当作共享密钥,用 HMAC-SHA256 去校验签名(算法混淆)。公钥本来就是公开的,所以一旦这样能通过,攻击者就能自己生成有效的签名。

防御方法相同:不信任头部的 alg,固定服务器期望的算法。先确定算法再校验,而不是根据拿到的密钥材料去选择校验方式。

3

确认令牌是给谁的、有效到何时

签名正确只能说明令牌是真的。它是否发给你、是否仍在有效期内,需要另外检查。

RFC 7519 规定:对于 aud,「当存在该声明时,如果处理方不能用 'aud' 中的某个值标识自己,则必须拒绝该 JWT」;exp 表示「在该时间及之后不得接受该 JWT」(nbf 则在另一端以相同方式起作用)。RFC 8725 还要求应用确认所用的密钥属于该签发者。如果跳过这些检查,原本合法签发给其他服务的令牌就可能被你的服务接受。

4

不要把机密放进载荷

要以"内容人人都能解码"为前提。密码、个人信息、API 密钥等机密不要放进去。只放标识符或权限等即便被看到也不致命的主张。

5

把有效期设短,并准备失效手段

访问用令牌要设为短命。过长的有效期会延长令牌被盗后的受害时间。如果需要失效,就把短命的访问令牌+服务器端管理的刷新/会话搭配使用。

6

使用强密钥,并存放在不会被盗的地方

签名密钥要足够强,不要复用,并保管在服务器端的安全位置。令牌本身也要守护好,比如不被 XSS 窃取、用带安全属性的 Cookie 传输(→ 注意经由 XSS 的窃取)。

本站观点:JWT 并不是适用于所有场景的会话机制

JWT 常被用于「保存登录状态」,但它有一个弱点:不擅长失效。只要签名正确且在有效期内,服务器基本上就会信任它,因此很难让登出或失效立即生效。

本站的立场是:不要把 JWT 当作长期有效的会话替代品。把短命的访问令牌,与服务器端可撤销的刷新/会话组合起来,就能兼顾 JWT 的优点(校验轻量)和失效的便利性。

注意:「能解码」不等于「正确」

JWT 的内容是可读的,所以只要贴进 JWT 解码器,任何人都能查看头部和载荷。但是解码(读取内容)与校验(确认真伪)完全是两回事。能解码并不能保证任何正确性。

判定令牌真伪的,永远是服务器端的签名校验。调试时查看内容很方便,但请不要误以为「能读出来就有效」。

JWT 与服务器端会话(Cookie)的区别

JWT 只是保存登录状态的方式之一。与服务器端会话相比,它在有些场景更合适,在另一些场景则不如后者。

项目服务器端会话(Cookie)JWT
状态保存在哪里服务器上(数据库或内存);Cookie 里只有 ID令牌内部;服务器只校验签名
登出 / 停用账号在服务器上删除即可立即生效除非另行实现撤销,否则在有效期内都会被接受
内容是否可见只有 ID,没有可读内容任何人都能解码读取
适合的场景运行在单一域名上的普通 Web 应用多个服务或 API 校验同一个令牌

对于只在一个域名上运行的 Web 应用,服务器端会话往往就够用了。如果选择 JWT,登录时签发新令牌,与防御会话固定是同一个原则。

真实案例:未检查算法的库

算法切换并不只是理论。被广泛使用的 Node.js JWT 库 jsonwebtoken 在 4.2.1 及更早版本中,没有正确检查令牌头部指定的算法,导致校验可以被绕过(CVE-2015-9235,已在 4.2.2 修复)。本应使用公钥校验的令牌,改用共享密钥(HS 系列)算法签名后也能通过——这正是上文所说的算法混淆。

教训有两点:

  • 保持库为最新版本。把锁文件贴进依赖漏洞扫描器,就能检查依赖中是否有已知漏洞
  • 不要依赖库的默认设置,在自己的代码中明确写出允许的算法

如何检查自己的令牌

把自家服务签发的令牌贴进 JWT 解码器,查看以下几点。解码在浏览器中完成,令牌不会被发送到服务器。即便如此,如果是生产环境中正在使用的令牌,检查后最好登出一次,让它失效。

  1. 头部的 alg 是否为期望的值(不是 none)?
  2. 是否有 exp,从 iat(签发时间)算起的有效时长是否符合用途?
  3. 是否有 aud 和 iss,且指向自家服务?
  4. 载荷中是否包含不希望被看到的内容,例如邮箱地址、电话号码、API 密钥?

服务器端的校验请在开发环境中用测试确认。改动了一个签名字符的令牌、alg 不同的令牌、已过期的令牌、发给其他服务的令牌(aud 不符)都应被拒绝——建议把这些写成自动化测试。

出处(一手资料)

  • IETF RFC 8725, "JSON Web Token Best Current Practices" — datatracker.ietf.org(固定算法、校验所有密码学运算、算法混淆、密钥须属于签发者)
  • IETF RFC 7519, "JSON Web Token (JWT)" — datatracker.ietf.org(exp、nbf、aud 的规范定义)
  • GitHub Advisory Database, "Verification Bypass in jsonwebtoken" (CVE-2015-9235) — github.com/advisories(受影响版本为 4.2.1 及更早,已在 4.2.2 修复)
  • OWASP HTML5 Security Cheat Sheet — cheatsheetseries.owasp.org(不要把会话标识符存放在 localStorage 中)

接下来阅读

FAQ

QJWT 的内容是加密的吗?
A

不是。标准 JWT 的头部和载荷并非加密,只是用 base64url 做了「编码」,任何持有令牌的人都能读到里面的内容。所以绝不能把密码、个人信息等机密放进载荷。JWT 保护的不是「内容的保密性」,而是「未被篡改(靠签名保证的完整性)」。

QJWT 中最危险的设置是什么?
A

不校验签名,或者接受「alg:none(无签名)」的实现。一旦允许,攻击者就能让自己随意改写内容的伪造令牌通过。防御方法是:在服务器端必须校验签名,并把允许接受的签名算法(alg)固定为期望的值。

Q解码和校验一样吗?
A

不一样。解码只是读取内容,任何人都能做(用 JWT 解码器即可查看)。校验则是服务器用密钥/公钥确认签名是否正确、是否在有效期内、签发者和受众是否正确的处理过程。「能解码=就是正确的令牌」是错误的。判定真伪的永远是服务器端的校验。

Q应该用 JWT 还是会话(Cookie)?
A

对于运行在单一域名上的普通 Web 应用,服务器端会话+Cookie 往往就够用了,登出或停用账号也能立即生效。JWT 适合多个服务或 API 校验同一个令牌的场景,或者从外部身份提供方(OAuth/OpenID Connect)接收令牌的场景。如果使用 JWT,请把有效期设短,并搭配服务器端可撤销的机制。

Q可以把 JWT 存进 localStorage 吗?
A

不推荐。localStorage 始终可以被 JavaScript 读取,只要存在一处 XSS 漏洞,令牌就可能被窃取。OWASP 也建议不要把会话标识符存放在 localStorage 中。在浏览器里,用带 HttpOnly、Secure、SameSite 属性的 Cookie 传输令牌更安全;使用 Cookie 时,还要同时做好 CSRF 防护。

QJWT 的「alg:none」是什么?
A

指头部中 alg(签名算法)被设为 none、也就是没有签名的 JWT。规范上允许这种形式,但用于登录等认证的令牌绝不能接受它,否则任何人都能制作改写了内容的令牌。防止方法是在服务器端把允许的算法固定为期望的值(例如只允许 RS256)。