跳转到主要内容

JWT

本文约需 2 分钟阅读

JWT (JSON Web Token) 是指将 JSON 格式的数据附加电子签名后编码为紧凑字符串的令牌。它于 2015 年作为 RFC 7519 标准化,被广泛用作 OAuth 2.0 的访问令牌以及单点登录 (SSO) 的断言。其最大特点是能够实现服务器端不保留会话信息的无状态认证,随着微服务架构的普及而被迅速采用。

三段式结构 - Header.Payload.Signature

JWT 由用点 (.) 分隔的三个部分构成。每个部分都经过 Base64URL 编码,人类无法直接阅读,但解码后即可以 JSON 形式确认其内容。

JWT 的结构:
Header.Payload.Signature
Header: 存储签名算法 (alg) 与令牌类型 (typ)
Payload: 存储用户 ID、过期时间 (exp)、签发者 (iss) 等声明
Signature: 用密钥对 Header + Payload 签名得到的值。用于检测篡改

JWS 与 JWE 的区别

JWT 这一术语在日常中被用作“签名令牌”的意思,但在规范上存在 JWS (JSON Web Signature) 与 JWE (JSON Web Encryption) 两种类型。

JWS (签名)

Payload 仅经过 Base64URL 编码,解码后任何人都能读取其内容。签名能够检测篡改,但无法对内容保密。通常被称为“JWT”的大多是这种 JWS 格式。

JWE (加密)

对 Payload 本身进行加密,只有持有解密密钥的人才能读取内容。其采用五段式结构 (Header、Encrypted Key、IV、Ciphertext、Authentication Tag)。在必须将机密信息包含在令牌中时使用。

在实务中 JWS 的使用占绝大多数。前提是 Payload 中不包含个人信息或机密数据,若必须包含,则应考虑使用 JWE,或者重新审视设计使其根本不包含在令牌中。

无状态认证的优点与风险

采用 JWT 的无状态认证中,服务器不保留会话信息。由于仅验证令牌签名即可完成认证,无需在 Redis 或数据库中管理会话令牌,因此横向扩展十分容易。

观点JWT (无状态)Cookie 会话 (有状态)
服务器端状态无 (信息包含在令牌本身)有 (需要会话存储)
可扩展性高 (任何服务器都可验证)需要会话共享机制
即时失效困难 (需要黑名单机制)容易 (只需删除会话)
令牌大小较大 (数百字节至数 KB)较小 (仅会话 ID)
CSRF 抵抗力使用 Authorization 头时具有抵抗力需要 SameSite 属性等对策

最大的风险在于“已签发的 JWT 无法即时失效”这一点。即使用户更改了密码,泄露的 JWT 仍可一直使用到过期为止。为缓解该问题,常见的做法是将访问令牌的过期时间设置得较短 (约 15 分钟),并通过刷新令牌重新签发。在必须即时失效的需求中,应在服务器端维护黑名单,或选择基于 Cookie 的会话管理。

针对 JWT 的代表性攻击

alg:none 攻击

将 Header 的 alg 字段改写为 "none",从而绕过签名验证的攻击。早期的库中有许多接受 alg:none 的实现。作为对策,应在服务器端以白名单方式明确指定允许的算法。

算法混淆攻击

将以 RS256 (非对称密钥) 签名的令牌的 alg 改写为 HS256 (对称密钥),并将公钥用作 HMAC 密钥的攻击。由于公钥任何人都能获取,攻击者便能生成有效的签名。

弱密钥

若 HS256 的密钥是短字符串或可猜测的值,便会被暴力破解确定密钥。必须使用至少 256 比特以上的随机密钥,并定期进行轮换。

会话劫持的手法同样适用于 JWT。将其与加密的基础知识一并理解十分重要。也请一并参阅会话劫持对策会话令牌窃取的防御措施以及 API 密钥管理的最佳实践Web 认证相关书籍 (Amazon)推荐用来进行系统性学习。

相关术语

这篇文章对您有帮助吗?

XHatena