JSON Web Token

JSON Web TokenJWT,建议读音 ,与英语单词“jot”同音)是一项拟议中的互联网标准,用于建立带有可选签名和/或可选加密的数据,其有效载荷中保存用于断言若干声明的 JSON。这些令牌可以使用共享秘密,或使用公钥/私钥进行签名。

例如,服务器可以生成一个带有“已作为管理员登录”这一声明的令牌,并将其提供给客户端。客户端随后便可使用该令牌来证明自己已以管理员身份登录。令牌可以由某一方的私钥(通常是服务器的私钥)签名,以便任何一方随后都能验证令牌是否合法。若另一方通过适当且可信的方式持有对应的公钥,则其也能够验证该令牌的合法性。这些会话令牌被设计为紧凑、URL安全、

JWT 依赖其他基于 JSON 的标准: 与 。

结构
; 标头
: 用于标识生成签名时所使用的算法。在下面的例子中,HS256 表示此令牌使用 HMAC-SHA256 进行签名。

: 常见的密码学算法包括使用 SHA-256 的 HMAC(HS256),以及使用 SHA-256 的 RSA 数字签名(RS256)。JWA(JSON Web Algorithms)RFC 7518 还为认证与加密引入了更多算法。
:{
"alg": "HS256",
"typ": "JWT"
}

; 有效载荷
: 包含一组声明。JWT 规范定义了七个已注册声明名称,它们是通常包含在令牌中的标准字段。

用途
在认证场景中,当用户成功登录后,通常会返回一个 JSON Web Token(JWT)。该令牌应通过安全机制发送给客户端,例如使用HTTP-only Cookie。通常不建议将 JWT 存储于浏览器的本地存储机制中,例如 localStorage 或 sessionStorage。这是因为运行在客户端的 JavaScript(包括浏览器扩展)可以访问这些存储机制,从而暴露 JWT 并危及安全性。若要在需要与跨来源 API 进行认证时使用 HTTP-only Cookie,较佳做法是使用 credentials 属性,告知浏览器在 Fetch 调用时自动将 Cookie 发送至外部 API,例如:

fetch('https://api.example.com/data', {
method: 'GET',
credentials: 'include' // 告知浏览器一并发送 Cookie 等凭证
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));

使用这种方式时,JWT 不会暴露给客户端侧 JavaScript;这是在遵循安全最佳实践的同时使用 JWT 的较佳方式。对于无人值守的进程,客户端也可以直接使用预共享密钥生成并签署自己的 JWT,然后将其传递给兼容 OAuth 的服务,如下所示:

POST /oauth2/token
Content-type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=eyJhb...

若客户端传递了有效的 JWT 断言,服务器将生成一个可用于调用应用程序的 access_token,并将其返回给客户端:

{
"access_token": "eyJhb...",
"token_type": "Bearer",
"expires_in": 3600
}

当客户端希望访问受保护的路由或资源时,用户代理通常会发送 JWT,一般是在 Authorization HTTP标头中使用 Bearer 方案。该标头内容可能如下所示:
Authorization: Bearer eyJhbGci...<snip>...yu5CSpyHI

这是一种无状态认证机制,因为用户状态并不会保存在服务器内存中。服务器上的受保护路由会检查 Authorization 标头中的 JWT 是否有效;若存在且有效,用户便可访问受保护资源。由于 JWT 是自包含的,所需信息都已包含其中,因此可减少多次查询数据库的需要。

标准字段
{| class="wikitable"
! 代码
! 名称
! 说明
|-
! colspan="2" | 标准声明字段
| 互联网草案定义了以下标准字段(“声明”),可用于 JWT 声明集合中。
|-
| iss
| 签发者
| 标识签发 JWT 的主体,例如组织名称或网站 URL。
|-
| sub
| 主题
| 标识 JWT 的主题,例如用户名或账号编号。
|-
| aud
| 受众
| 标识 JWT 的预期接收方。每个预期会处理该 JWT 的主体都必须在受众声明中以某个值标识自己。若处理该声明的主体在存在 aud 声明的情况下,无法以其中某个值标识自己,则该 JWT 必须被拒绝。
|-
| exp
| 过期时间
| 标识 JWT 在该时间及之后不得再被接受处理。其值必须为 NumericDate: 即整数或小数,表示自 00Z 起经过的秒数。
|-
| nbf
| 生效时间
| 标识 JWT 将从何时开始被接受处理。该值必须为 NumericDate。
|-
| iat
| 签发时间
| 标识 JWT 被签发的时间。该值必须为 NumericDate。
|-
| jti
| JWT ID
| 令牌的区分大小写唯一标识符,即使在不同签发者之间也应保持唯一。
|-
! colspan="2" | 常用标头字段
| 以下字段常用于 JWT 的标头中。
|-
| typ
| 令牌类型
| 若存在,则必须设为一个已注册的 [https://www.iana.org/assignments/media-types/media-types.xhtml IANA 媒体类型] 。
|-
| cty
| 内容类型
| 若使用嵌套签名或嵌套加密,建议将其设为 JWT;否则应省略此字段。

实现
JWT 已有许多语言与框架的实现,包括但不限于:

  • .NET(C#、VB.Net 等)
  • C
  • C++
  • Clojure
  • Common Lisp
  • Dart
  • Elixir
  • Erlang
  • Go
  • Haskell
  • Java
  • JavaScript
  • Julia
  • Lua
  • Node.js
  • OCaml
  • Perl
  • PHP
  • PL/SQL
  • PowerShell
  • Python
  • Racket
  • Raku
  • Ruby
  • Rust
  • Scala
  • Swift

漏洞
JSON Web Token 可包含会话状态。但如果项目需求允许在 JWT 到期前使会话失效,那么服务就不能再仅凭令牌本身信任其中的断言。若要验证令牌中记录的会话未被撤销,则必须将令牌断言与进行比对。这样一来,令牌就不再是无状态的,从而削弱了 JWT 的主要优势。

安全顾问 Tim McLean 曾报告,某些 JWT 函式库在使用 alg 字段验证令牌时存在漏洞,最常见的情况是接受了 alg=none 的令牌。尽管这些漏洞后来已被修补,McLean 仍建议彻底弃用 alg 字段,以防止类似的实现混淆。尽管如此,现实中仍不断发现新的 alg=none 漏洞;在 2018 至 2021 年间,已有四个CVE与此原因有关。

若设计得当,开发者可以通过采取以下预防措施来应对算法漏洞:

  • 不要仅凭 JWT 标头驱动验证流程
  • 了解相关算法(避免只依赖 字段)
  • 使用适当大小的密钥

2017 年,一些 JWT 函式库被发现容易受到无效椭圆曲线攻击。

也有人认为,由于标准中提供了大量不同的加密算法与选项,JSON Web Token 难以被安全地使用,因此网页前端与后端都应改用其他替代标准。

参见

  • API密钥
  • 访问令牌
  • HTTP基本认证
  • 摘要访问认证
  • HTTP头字段

*

参考文献
*

  • [https://jwt.io/ jwt.io] — 由 Auth0 维护的 JWT 专门网站,提供工具与文档

评论 (0)

  • 还没有评论,来抢沙发吧。