1. JWT 介绍
JSON web Token(JWT)是一个开放式标准 (RFC 7519), 它定义了一种紧凑(Compact) 且自包含 (Self-contained) 的方式, 用于在各方之间以 JSON 对象安全传输信息. 这些信息可以通过数字签名进行验证和信任. 可以使用秘密 (使用 HMAC 算法) 或使用 RSA 的公钥 / 私钥对对 JWT 进行签名.
虽然 JWT 可以加密以提供各方之间的保密性, 但我们将重点关注已签名的令牌. 签名的令牌可以验证其中包含的索赔的完整性, 而加密令牌隐藏来自其他方的索赔. 当令牌使用公钥 / 私钥对进行签名时, 签名还证明只有持有私钥的方是签名方.
我们来进一步解释一些概念:
Compact(紧凑):
由于它们尺寸较小, JWT 可以通过 URL,POST 参数或 HTTP 标头内发送. 另外, 尺寸越小意味着传输速度越快.
Self-contained(自包含):
有效载荷 (Playload) 包含有关用户的所有必需信息, 避免了多次查询数据库.
2. JWT 适用场景
Authentication(鉴权):
这是使用 JWT 最常见的情况. 一旦用户登录, 每个后续请求都将包含 JWT, 允许用户访问该令牌允许的路由, 服务和资源. 单点登录是当今广泛使用 JWT 的一项功能, 因为它的开销很小, 并且能够轻松地跨不同域使用.
Information Exchange(信息交换):
JSON Web Tokens 是在各方之间安全传输信息的好方式. 因为 JWT 可以签名: 例如使用公钥 / 私钥对, 所以可以确定发件人是他们自称的人. 此外, 由于使用标头和有效载荷计算签名, 因此您还可以验证内容是否未被篡改.
3. JWT 结构
在紧凑的形式中, JWT 包含三个由点 (.) 分隔的部分, 它们分别是:
- Header
- Payload
- Signature
JWT 结构通常如下所示:
xxxxx.yyyyy.zzzzz
下面我们分别来介绍这三个部分:
Header
Header 通常由两部分组成: 令牌的类型, 即 JWT. 和常用的散列算法, 如 HMAC SHA256 或 RSA.
例如:
- {
- "alg": "HS256",
- "typ": "JWT"
- }
Header 部分的 JSON 被 Base64Url 编码, 形成 JWT 的第一部分.
Payload
这里放声明内容, 可以说就是存放沟通讯息的地方, 在定义上有 3 种声明(Claims):
Registered claims(注册声明):
这些是一组预先定义的声明, 它们不是强制性的, 但推荐使用, 以提供一组有用的, 可互操作的声明. 其中一些是: iss(发行者),exp(到期时间),sub(主题),aud(受众)等.#Registered Claim Names# https://tools.ietf.org/html/rfc7519#section-4.1
Public claims(公开声明):
这些可以由使用 JWT 的人员随意定义. 但为避免冲突, 应在 IANA JSON Web 令牌注册表中定义它们, 或将其定义为包含防冲突命名空间的 URI.
Private claims(私有声明):
这些是为了同意使用它们但是既没有登记, 也没有公开声明的各方之间共享信息, 而创建的定制声明.
Playload 示例如下:
- {
- "sub": "1234567890",
- "name": "John Doe",
- "admin": true
- }
Playload 部分的 JSON 被 Base64Url 编码, 形成 JWT 的第二部分.
Notice:
请注意, 对于已签名的令牌, 此信息尽管受到篡改保护, 但任何人都可以阅读. 除非加密, 否则不要将秘密信息放在 JWT 的有效内容或标题元素中. 这也是很多文章争论 jwt 安全性原因, 不要用 JWT 取代 Server-side 的 Session 状态机制. 详情请阅读这篇文章: Stop Using Jwt For Sessions http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/ .
Signature
第三部分 signature 用来验证发送请求者身份, 由前两部分加密形成.
要创建签名部分, 您必须采用编码标头, 编码有效载荷, 秘钥, 标头中指定的算法并签名.
例如, 如果你想使用 HMAC SHA256 算法, 签名将按照以下方式创建:
- HMACSHA256(
- base64UrlEncode(header) + "." +
- base64UrlEncode(payload),
- secret)
3. JWT 实践
JWT 输出的是三个由点分隔的 Base64-URL 字符串, 可以在 HTML 和 HTTP 环境中轻松传递, 而与基于 XML 的标准 (如 SAML) 相比, 它更加紧凑.
以下 JWT 示例, 它具有先前的标头和有效负载编码, 并且使用秘钥进行签名.
我们可以使用 jwt.io 调试器来解码, 验证和生成 JWT:
4.JWT 工作原理
在身份验证中, 当用户使用他们的凭证成功登录时, JSON Web Token 将被返回并且必须保存在本地(通常在本地存储中, 但也可以使用 Cookie), 而不是在传统方法中创建会话 服务器并返回一个 cookie.
关于存储令牌 (Token) 的方式, 必须考虑安全因素.
参考: #Where to Store Tokens# https://auth0.com/docs/security/store-tokens
无论何时用户想要访问受保护的路由或资源, 用户代理都应使用承载方案发送 JWT, 通常在请求头中的 Authorization 字段, 使用 Bearer schema:
Authorization: Bearer <token>
这是一种无状态身份验证机制, 因为用户状态永远不会保存在服务器内存中. 服务器受保护的路由将在授权头中检查有效的 JWT, 如果存在, 则允许用户访问受保护的资源. 由于 JWT 是独立的, 所有必要的信息都在那里, 减少了多次查询数据库的需求.
这使得我们可以完全依赖无状态的数据 API, 甚至向下游服务提出请求. 无论哪些域正在为 API 提供服务并不重要, 因此不会出现跨域资源共享 (CORS) 的问题, 因为它不使用 Cookie.
Notice:
请注意, 使用已签名的令牌, 令牌中包含的所有信息都会暴露给用户或其他方, 即使他们无法更改它. 在 JWT 中, 不应该在 Playload 里面加入任何敏感的数据, 比如像密码这样的内容. 如果将用户的密码放在了 JWT 中, 那么怀有恶意的第三方通过 Base64 解码就能很快地知道你的密码了.
5. 常见问题
JWT 安全嗎?
Base64 编码方式是可逆的, 也就是透过编码后发放的 Token 内容是可以被解析的. 一般而言, 我们都不建议在有效载荷内放敏感讯息, 比如使用者的密码.
JWT Payload 內容可以被伪造嗎?
JWT 其中的一个组成内容为 Signature, 可以防止通过 Base64 可逆方法回推有效载荷内容并将其修改. 因为 Signature 是经由 Header 跟 Payload 一起 Base64 组成的.
如果我的 Cookie 被窃取了, 那不就表示第三方可以做 CSRF 攻击?
是的, Cookie 丢失, 就表示身份就可以被伪造. 故官方建议的使用方式是存放在 LocalStorage 中, 并放在请求头中发送.
空间及长度问题?
JWT Token 通常长度不会太小, 特别是 Stateless JWT Token, 把所有的数据都编在 Token 里, 很快的就会超过 Cookie 的大小 (4K) 或者是 URL 长度限制.
Token 失效问题?
无状态 JWT 令牌 (Stateless JWT Token) 发放出去之后, 不能通过服务器端让令牌失效, 必须等到过期时间过才会失去效用.
假设在这之间 Token 被拦截, 或者有权限管理身份的差异造成授权 Scope 修改, 都不能阻止发出去的 Token 失效并要求使用者重新请求新的 Token.
6. JWT 使用建议
不要存放敏感信息在 Token 里.
Payload 中的 exp 时效不要设定太长.
开启 Only Http 预防 XSS 攻击.
如果担心重播攻击 (replay attacks ) 可以增加 jti(JWT ID),exp(有效时间) Claim.
在你的应用程序应用层中增加黑名单机制, 必要的时候可以进行 Block 做阻挡(这是针对掉令牌被第三方使用窃取的手动防御).
- [1] Stop using JWT for sessions:
- http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/
- [3] Use JWT The Right Way!:
- https://stormpath.com/blog/jwt-the-right-way
[2] JSON Web Token 维基百科:
https://en.wikipedia.org/wiki/JSON_Web_Token
来源: https://www.cnblogs.com/mantoudev/p/8994341.html