本文主要普及一下 JWT(JSON Web Token) 的相关知识以及安全性问题。

01

JWT 简介

JWT 是 JSON web Token 的缩写,它是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放式标准 (RFC 7519),该 token 被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO) 场景。JWT 的声明一般被用在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的业务逻辑所必须声明信息,该 token 也可被直接用于认证,也可用作加密。

02

JWT 应用场景

(1) 授权

这个是使用 JWT 最常见的场景,一旦用户登录,后续每个请求都将包括 JWT,从而允许用户访问该令牌允许的路由、服务以及资源。单点登录 (SSO) 是目前使用 JWT 最广泛的一个场景,JWT 的方式可以让用户在不同的域中轻松灵活使用。

(2) 构成

JWT 由三部分构成,第一部分我们称它为头部(header), 第二部分我们称其为载荷(payload),第三部分是签证(signature).

JWT 的头部承载两部分信息:

  • 声明类型,这里是 jwt,声明加密的算法 通常直接使用 HMAC SHA256。
  • 完整的头部就像下面这样的 JSON:
  1. {
  2. 'typ': 'JWT',
  3. 'alg': 'HS256'
  4. }

然后将头部进行 base64 加密(该加密是可以对称解密的), 构成了第一部分。

  1. eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分:

  • 标准中注册的声明

  • 公共的声明

  • 私有的声明

标准中注册的声明 (建议但不强制使用) :

  • iss: jwt 签发者
  • sub: jwt 所面向的用户
  • aud: 接收 jwt 的一方
  • exp: jwt 的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该 jwt 都是不可用的.
  • iat: jwt 的签发时间
  • jti: jwt 的唯一身份标识,主要用来作为一次性 token, 从而回避重放攻击。

公共的声明 :

公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息. 但不建议添加敏感信息,因为该部分在客户端可解密。

私有的声明 :

私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为 base64 是可解密的,意味着该部分信息可以归类为明文信息。

定义一个 payload:

  1. {
  2. "sub": "1234567890",
  3. "name": "John Doe",
  4. "admin": true
  5. }

然后将其进行 base64 加密,得到 JWT 的第二部分。

  1. IxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9sig

jwt 的第三部分是一个签证信息,这个签证信息由三部分组成:

  • header (base64 后的)
  • payload (base64 后的)
  • secret

这个部分需要 base64 加密后的 header 和 base64 加密后的 payload 使用. 连接组成的字符串,然后通过 header 中声明的加密方式进行加盐 secret 组合加密,然后就构成了 jwt 的第三部分。

  1. // javascript
  2. var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
  3. var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

将这三部分用.连接成一个完整的字符串, 构成了最终的 JWT:

  1. eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

(4) JWT 应用

一般是在请求头里加入Authorization,并加上Bearer标注:

  1. fetch('api/user/1', {
  2. headers: {
  3. 'Authorization': 'Bearer ' + token
  4. }
  5. })

服务端会验证 token,如果验证通过就会返回相应的资源。整个流程就是这样的:

安全攻防 | JWT认知与攻击 - 图1

参考数据包如下:

  1. POST / HTTP/1.1
  2. Host: xxxx
  3. Connection: close
  4. Content-Length: 0
  5. Pragma: no-cache
  6. Origin: xxxx
  7. Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJuYW1lIjoi55yB5YWs5a6J5Y6FZ29uZ2FudGluZ19hZG1pbiIsInVpZCI6ImdvbmdhbnRpbmdfYWRtaW4iLCJ1c2VydHlwZSI6IjIiLCJmaXJzdFVwZGF0ZVB3ZCI6IjAiLCJpYXQiOjE2MDA2NzAwOTIsIm9yZ0lkIjoiNzVya2d2amVzYy1leC12dWZhZHd5cSJ9.CIYHEQYrGnmeJMOnjIL7F7yA_sqWm6LC2hZFgzsXwlexbs3PgYhcUx-iL7g6vL4_rwi5hyUX19AgOK9bfzmhag(jwt特征,带有Bearer字段)
  8. Accept: application/json, text/plain, */*
  9. Cache-Control: no-cache, no-store, must-revalidate
  10. trim: true
  11. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36
  12. Expires: 0
  13. Referer:
  14. Accept-Language: zh-CN,zh;q=0.9
  15. Cookie: JSESSIONID=C7J4Eyr9tSIeLmKY9xEe_wIpPLEA78gNmaKc1XKm; isClear=1; portal-token=eyJhbGciOiJIUzUxMiJ9.eyJuYW1lIjoi55yB5YWs5a6J5Y6FZ29uZ2FudGluZ19hZG1pbiIsInVpZCI6ImdvbmdhbnRpbmdfYWRtaW4iLCJ1c2VydHlwZSI6IjIiLCJmaXJzdFVwZGF0ZVB3ZCI6IjAiLCJpYXQiOjE2MDA2NzAwOTIsIm9yZ0lkIjoiNzVya2d2amVzYy1leC12dWZhZHd5cSJ9.CIYHEQYrGnmeJMOnjIL7F7yA_sqWm6LC2hZFgzsXwlexbs3PgYhcUx-iL7g6vL4_rwi5hyUX19AgOK9bfzmhag。

安全攻防 | JWT认知与攻击 - 图2

(5) Jwt 安全问题

关于 jwt 的使用安全性,其实国外已经有很详细的研究文章进行介绍了,具体参考地址: https://research.securitum.com/jwt-json-web-token-security/.

freebuf 翻译版本地址: https://www.freebuf.com/vuls/219056.html

使用 base64 解密该密钥如下所示:

  1. eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

安全攻防 | JWT认知与攻击 - 图3

所见,使用此 “API 密钥”(其主要内容在 payload 中),我们可以实现身份验证(我有与 API 进行通信的特权)和授权(在上面的有效负载中,您可以看到示例操作)可以由密钥的所有者执行)。基本是常见的场景下,jwt 是用来做身份校验的,识别请求者的身份以及用于鉴权。那么,从安全性的角度来看,至少存在两个潜在的问题。

1、缺乏机密性

我们能够轻松解码有效载荷 payload(和报头 header)。有时间就是这样要求的,但是当我们要求对令牌中发送的数据进行保密时,有一种更好的方法可以做到这一点:JWE(JSON Web 加密)。

2、用户插入另一个操作(例如删除)并绕过授权的潜在可能性。

在这种情况下,解决方案是在令牌中使用签名(请注意,在上面的示例中,我们看到了 “签名”)。标头中指示的 HS256 算法是标准的 HMAC-SHA256 –一种确保整个消息完整性的机制(由于这样,用户无法更改有效负载)在签名验证期间检测篡改)。要配置 HS256,您需要生成一个密钥(字符串)并将其放入 API 配置中。

安全攻防 | JWT认知与攻击 - 图4

综上所述,JWT 看上去比 API 密钥灵活得多 - 您可以轻松地传输任何数据,确保其完整性,并在必要时保持机密性。此外,所有信息(秘密密钥除外)都可以位于令牌本身中。但是,世界上没有十全十美

问题一:JWT 是一种非常复杂的机制。JWT / JWS / JWE / JWK,多种密码算法,两种不同的编码(序列化),压缩方式,一个以上签名的可能性,对多个接收者的加密 - 这些仅是几个示例。所有与 JWT 相关的规范都有 300 多个页面!

问题二:根据 JWT 的正式规范,虽然通常假定 “适当的” JWT 是带有签名(JWS)的 JWT,但是签名不是强制性的

因此,会有这种 header

  1. {
  2. alg “: none “,
  3. typ “: JWT
  4. }

有趣的是,”none” 这个算法配置是根据 RFC 实现的两种算法之一,在 JSON Web 算法[JWA]中指定的签名和 MAC 算法中,仅 “HS256” 和 “none” 通过符合 JWT 的实现。

03

JWT 漏洞攻击思路

方法一:修改签名算法

攻击者可以获得一个 JWT(带有签名),对其进行更改(例如,添加新权限等),然后将其放在标头 {“alg”:”none”} 中。然后将整个内容发送到 API(带或不带签名)。这时候,服务器应该接受这样的令牌吗?从理论上讲是可以的,但是它将破坏 JWT 签名的整个思想。然而,这样的情况真的发生了。

在许多图书馆实施 JWTs:

https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/,cvedetail:https://www.cvedetails.com/cve/CVE-2018-1000531/。

安全攻防 | JWT认知与攻击 - 图5

方法二:删除签名

如果标头中有一个签名算法(例如 HS256 或 HS512),但是我们从令牌中删除了整个签名部分,会发生什么?

案例链接https://github.com/FusionAuth/fusionauth-jwt/issues/2

正如你所看到的,有时候这样的令牌就会被验证,这是比上面方法更危险的配置。JWTDecoder.decode 中的输入验证漏洞,即使缺少有效签名,该漏洞也可能导致 JWT 被解码并因此被隐式验证。

安全攻防 | JWT认知与攻击 - 图6

方法三:插入错误信息

如果攻击者不知道如何创建适当的签名,也许会将其插入错误消息中https://github.com/jwt-dotnet/jwt/issues/61。

安全攻防 | JWT认知与攻击 - 图7

因此,如果有人更改了有效负载并将此类令牌发送给服务器,则服务器会礼貌地通知我们有关信息,并提供与我们的有效负载匹配的正确令牌。为了使系统正常运行,必须将服务器配置为向用户显示异常,虽然这很普遍,但是这是个不安全的配置。

另一个例子https://auth0.com/docs/security/bulletins/cve-2019-7644

低于 1.0.4 的所有 Auth0-WCF-Service-JWT NuGet 软件包版本 均在 JWT 签名验证失败时发出的错误消息中包含有关预期 JWT 签名的敏感信息。

方法四:破解 HMAC 密钥

由于加密字的强度过低,因此 hmac 的密钥可以被破解。破解 jwt 的加密字,标准方法采用 API 生成的令牌并运行经典的蛮力 / 字典 / 混合攻击。一次迭代需要计算两个 SHA256 哈希(这是 HMAC-SHA256 的工作方式),并且还有一些工具可以使整个操作自动化,例如 hashcat 使用 GPU 实现 JWT 密钥的破解。借助几个快速的 GPU,您可以实现每秒超过十亿次检查的速度。而且,整个操作可以脱机完成,而无需与 API 进行任何交互(足以获得一个带有签名的任意令牌)。

hashcat -m 16500 jwt.txt -a 3 -w 3 ?a?a?a?a?a?a

安全攻防 | JWT认知与攻击 - 图8

破解工具:

c-jwt-cracker https://github.com/brendan-rius/c-jwt-cracker

hashcat

PyJWT

library https://github.com/jpadilla/pyjwt,https://github.com/hashcat/hashcat/issues/1057

因此,密钥太弱会被爆破,那么我们该用什么强度的密钥呢?

此算法必须使用与哈希输出大小相同的密钥(例如,“HS256” 为 256 位)或更大。(此要求基于 NIST SP 800-117 [NIST.800-107]的第 5.3.4 节(HMAC 密钥的安全性影响),其中规定,有效的安全性强度是密钥的安全强度中的最小值。两倍于内部哈希值的大小)。

方法五:利用签名方法

很多 jwt 的安全问题来源于复杂的标准。到目前为止,JWS 签名算法已经有 HMAC 和 SHA256 函数,但是这并不是唯一的选择,各种签名的描述可以在这个链接里找到https://auth0.com/blog/json-web-token-signing-algorithms-overview/

常见的选择是使用非对称算法 - RSA。在这种情况下,我们将在 header 中的 “alg”:”RS512” 或 “alg”:”RS256” 中看到。

提醒一下:RSA 私钥用于签名,与其关联的公钥可以验证签名。因此,在这种情况下,我们生成了一对 RSA 密钥,而不是对称密钥(如 HS256 算法中的对称密钥)。

如果您第一次看到 RS512 或 RS256,您可能会想到使用 512 或 256 位 RSA 密钥的要求?

但是,这样的密钥可以以最小的成本和时间来破坏(请参阅:https ://eprint.iacr.org/2015/1000.pdf 或https://www.theregister.co.uk/2010/01/07/rsa_768_broken/)。

即使是 1024 位 RSA 密钥也不被认为是安全的。幸运的是,这仅指向与 RSA 结合使用的特定 SHA 函数。例如,RS512 表示 RSA 加 SHA512 功能。但是 RSA 密钥呢?长度由生成它的人员设置,这是另一个潜在的问题(此外,在不同的在线教程中,您可以使用 OpenSSL 并生成 1024 位密钥来找到特定的命令)

回到这一点,使用 RSA 算法,我们至少还有一个有趣的安全问题。如我之前所写,公钥用于签名验证,因此通常会在 API 配置中将其设置为 verify_key。在这里,值得注意的是,对于 HMAC,我们只有一个对称密钥同时用于签名和验证。

攻击者如何伪造 JWT 令牌?

1、他获得了一个公共密钥(它的名字表明它可以公开使用)。有时,它在 JWT 自身内部传输。

2、使用 header 中设置的 HS256 算法发送令牌(有效载荷已更改)(即 HMAC,而不是 RSA),并使用公共 RSA 密钥对令牌进行签名。是的,这里没有错误–我们使用公共 RSA 密钥(以字符串形式给出)作为 HMAC 的对称密钥。

3、服务器接收令牌,检查将哪种算法用于签名(HS256),验证密钥在配置中设置为公共 RSA 密钥。

4、签名经过验证(因为使用了完全相同的验证密钥来创建签名,并且攻击者将签名算法设置为 HS256)。

安全攻防 | JWT认知与攻击 - 图9

尽管我们打算仅使用 RSA 验证令牌的签名,但有可能由用户提供签名算法。因此,要么我们只强制一个选定的签名算法(我们不提供通过更改令牌来更改它的可能性),要么让我们为我们支持的每种签名算法提供单独的验证方法(和密钥!)

下面是这个漏洞的真实案例:https://www.cvedetails.com/cve/CVE-2016-10555/。

方法六:信任攻击者密钥

攻击者可以在令牌中提供自己的密钥,然后 API 会使用该密钥进行验证!请参阅 CVE-2018-0114 中的节点 node-jose 漏洞的细节:

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-0114

该漏洞是由于遵循 JSON Web 令牌(JWT)的 JSON Web 签名(JWS)标准而导致的节点丢失。该标准指定可以将表示公共密钥的 JSON Web 密钥(JWK)嵌入 JWS 的标头中。然后将此公钥信任进行验证。攻击者可以通过以下方法来伪造有效的 JWS 对象:删除原始签名,向标头添加新的公钥,然后使用与该 JWS 标头中嵌入的公钥关联的(攻击者拥有的)私钥对对象进行签名,从而利用此漏洞早于 2016 年,在 Go-jose 库(https://mailarchive.ietf.org/arch/msgif/jose/gQU_C_QURVuwmy-Q2qyVwPLQlcg)中检测到类似的漏洞。

方法七:恢复私钥

在这里,您可以从几种算法中选择(消息本身的加密或用于加密消息的对称密钥的加密)。再次,有一些有趣的研究,即 JWE 的几种实现使攻击者可以恢复私钥:https://blogs.adobe.com/security/2017/03/critical-vulnerability-uncovered-in-json-encryption.html。更具体地说,ECDH-ES 算法的实现存在问题(顺便说一下,在相关的 RFC 文档中是推荐级别 https : //tools.ietf.org/html/rfc7518)。

也许以前的漏洞只是一个意外?让我们在 GCM 模式下查看 AES:https ://rwc.iacr.org/2017/Slides/nguyen.quan.pdf。谷歌研究人员正在 JWT 的背景下写这篇文章,他们总结道:GCM 很脆弱,但很少检查其实现。

我们还可以选择带有 PKCS1v1.5 填充的 RSA 算法。它出什么问题了?自 1998 年以来该问题已经知道:ftp://ftp.rsa.com/pub/pdfs/bulletn7.pdf。而有些人 概括起来是这样的:https://blog.cryptographyengineering.com/2012/09/06/on-provable-security-of-tls-part-1/

PKCS#1v1.5 非常棒–如果您要教授关于如何攻击密码协议的课程。

使用 JWE 会永远注定失败吗?当然不是,但是值得验证我们是否使用了适当的安全加密算法(及其安全实现)。

现在,我们对众多选择感到有些不知所措。毕竟,我们只想在 API 端 “解码” 令牌并使用其中包含的信息。但是请记住,“decode”并不总是与 “verify” 相同,但是不同的库可能提供不同的功能来解码和 / 或验证令牌。可以在下面链接找到此类问题或疑问的示例。https://github.com/auth0/jwt-decode/issues/4

简而言之,如果我使用 encode()函数,则可能只对 BASE64URL 的有效负载(或标头)进行解码,而无需进行任何验证。验证可以是一个单独的函数,尽管它也可以内置在 decode()中。有时,是用户要求这种选项(在下面引用的情况下),有人要求重载 decode()方法,以便它也可以接受令牌本身(没有密钥):

方法八:上下文相同令牌

JWT 经常指出的优点之一是,无需执行对数据库的查询,即可实现身份验证(或授权 - 取决于将使用整个上下文的上下文)。此外,我们可以在几个独立的服务器(API)上并行执行此操作。毕竟,仅令牌的内容就足以在此处做出决定。它还有一个缺点–如果许多服务器上可用的签名密钥以某种方式泄漏了怎么办?当然,有可能生成使用适当密钥进行验证的所有机器所接受的正确签名的令牌。攻击者可以从中获得什么?例如,未经授权访问 API 函数或其他用户帐户。

在这种情况下,可以使用规范本身定义的某些参数:iss(发出者)和 aud(听众)。多亏了他们,令牌才被我们的特定接收者接受。

{

“iss” = “my_api “,

“login” = “manager “,

“aud” = “store_api “

}

方法九:重放 JWT

如果特定令牌只能使用一次怎么办?让我们想象一个场景,当用户编写一个生成的令牌以执行我们 API 中的 DELETE 方法时。然后,例如一年后(理论上他不再拥有相应的权限)之后,他尝试再次使用它(所谓的重播攻击)。

为此,请使用以下声明:jti 和 exp。Jti(JWT ID)是令牌标识符,必须是唯一的,而 exp 是令牌到期日期的定义。这两个字段的组合将使我们在适当程度上缩短令牌的有效性及其唯一性。

但是,值得注意的是,我们是否正确实施了这两个部分。现在看看根本没有考虑 exp 值的 bug(https://github.com/jwt-dotnet/jwt/issues/134)。

JWT 不会在. NetCore 中抛出 ExpiredTokenException

库开发人员使用到期声明(不在 JWT 规范中)执行到期检查;报告后,该错误已得到纠正。

方法十:定时攻击签名

如果通过具有正确签名的字节接一个字节地检查来自 JWS 的签名(由接受 JWS 的一方生成),并且如果验证在第一个不一致的字节上完成,则我们可能会受到时间攻击。

请注意,在这种情况下,我们拥有的匹配字节越多,需要的比较就越多,因此响应所需的时间越长。

可以通过生成连续的签名来观察响应时间,从签名的第一个字节开始,然后再移至第二个签名。有关此类攻击的确切描述,请参见:https://hackernoon.com/can-timing-attack-be-a-practical-security-threat-on-jwt-signature-ba3c8340dea9

04

参考文章

https://jwt.io/

https://research.securitum.com/jwt-json-web-token-security/

https://www.jianshu.com/p/576dbf44b2ae

https://www.freebuf.com/tag/JWT

https://www.freebuf.com/vuls/211842.html

  • 往期推荐 -

渗透测试之 XXE 漏洞

站库分离常规渗透思路总结

【推荐书籍】
https://mp.weixin.qq.com/s?__biz=Mzg4MzA4Nzg4Ng==&mid=2247491234&idx=1&sn=01d826d7f393a56e5e4874d14ceeb497