理解并使用 JWT

JWT 简介

JWT(JSON Web Token) 是一个基于 JSON 的开放标准,它允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。

JWT 本质上就是一串字符串,它由

  1. Header
  2. Payload
  3. Signature

这三个部分组成,每个部分之间使用.进行分隔。

关于 JWT 的更多介绍可见 https://jwt.io/introduction/

jwt

JWT 使用流程

jwt-diagram

整个流程图很清晰明朗,但是在第三步和第四步之间,客户端是需要保存用户状态的,那么JWT 是如何存储在客户端的呢?

浏览器端 JWT 存储方案

1. HTML5 WebStorage: localStorage or sessionStorage

使用这种方案时,服务端一般将token放入response body中发送给客户端:

HTTP/1.1 200 OK
{
  "token":"eyJhbGciOiJIUzI1R5cCIpXVCJ9.eyJpZCI6MSwiZW1W4MIjcwMDk2fQ.aYC_idpEATBit6QmOiNSTn_SY8v0o"
}

localStorage 和 sessionStorage 是 HTML5 的特性,使用起来非常简单。

localStorage.setItem('token', response.body.token);

2. Cookie Storage

Cookie 的工作机制是用户识别及状态管理。调用 Cookie 时,由于可校验 Cookie 的有效期,以及发送方的域、路径、协议等有效信息,所以正规发布的 Cookie 内的数据不会因来自其他站点和攻击者的攻击而泄露。为 Cookie 服务的 HTTP header 有以下两种:

字段名 说明 header 字段类型
Set-Cookie 开始状态管理所使用的 Cookie 信息 响应字段
Cookie 服务端接收到的 Cookie 信息 请求字段

验证用户信息成功后,服务端会将 JWT 放在HTTP header 中的Set-Cookie字段

HTTP/1.1 200 OK
Set-Cookie: access_token=eyJhbGciOiJIUzI1R5cCIpXVCJ9.eyJpZCI6MSwiZW1W4MIjcwMDk2fQ.aYC_idpEATBit6QmOiNSTn_SY8v0o; Secure; HttpOnly;

这样一来,当客户端向服务端发送请求时,请求中的Cookie字段会包含从服务端接收到的Cookie

Which one is better?

使用 WebStorage 存储token,那么任何运行在该网站上的 JS 代码都可以访问到token。如果网站遭到 XSS 攻击,那么用户信息就变得不安全了。所以包括 White Hat 在内的很多组织都建议不要将一些重要信息存储在 localStorage 和 sessionStorage 中。另外,在传输用户信息和 JWT 的过程中,数据也不是安全的,所以最好能将 Web 应用的升级成 HTTPS,使用 SSL 对通信进行加密。

使用HttpOnly属性的 cookie,是无法被 JS 代码获取的。同时,给 Cookie 增加Secure属性还可以确保 cookie 只能通过 HTTPS 传输。JWT 具有自包含的特性,所以携带 JWT 的 cookie 无需在服务端存储,开发者不必担心使用 cookie 会破坏 RESTful 的最佳实践。但是面对 CSRF 攻击,Cookie 也能被恶意窃取。默认情况下,无法通过 HTTP 跨域发送 cookie,若要跨域,需在Access-Control-Allow-Origin字段中指定明确的域名。

关于这两种方案的比较,这篇文章做了比较全面的描述:Where to Store Your Jwts

通过 Authorization Header 将发送 JWT

若使用 WebStorage 作为存储方案,那么就需要 JWT 就需要通过Authorization头字段来发送。Authorization 由两部分组成

Authorization: <type> <credentials>

type 指的是验证类型,常见的有以下几种:

  • Basic (see RFC 7617, base64-encoded credentials.),
  • Bearer (see RFC 6750, bearer tokens to access OAuth 2.0-protected resources),
  • Digest (see RFC 7616, only md5 hashing is supported in Firefox, see bug 472823for SHA encryption support),
  • HOBA (see RFC 7486 (draft), HTTP Origin-Bound Authentication, digital-signature-based),

每个验证类型的不同之处在于它们的适用范围(客户端类型和服务端类型),以及它们的加密强度。

根据RFC,传输 JWT 选择的 Bearer 类型。JWT 与 类型之间以空格分隔。

Authorization: Bearer eyJhbGciOiJIUzI1R5cCIpXVCJ9.eyJpZCI6MSwiZW1W4MIjcwMDk2fQ.aYC_idpEATBit6QmOiNSTn_SY8v0o; Secure; HttpOnly;

Use JWT or not ?

在 JWT 之前,人们往往是通过 cookie + session 的方式来解决服务器如何识别用户的问题:

cookie-session

通过比较可以看出,使用 JWT 可以省去服务端读取 Session 的操作,它可以充分依赖无状态 API,更加符合 RESTful 规范。

看起来,JWT 十分美好。

但是!综合来看,还是有其局限性,已经有人呼吁停止将 JWT 用于会话机制。

Stop using jwt for sessions这篇文章将 JWT 的优劣分析的很详尽,也说明了 JWT 的真正使用场景。


参考资料:

  1. JWT introduction
  2. Where to Store Your Jwts
  3. Stop using jwt for sessions
Show Comments