浅谈JWT安全
浅谈JWT安全
JWT是为了解决HTTP会话的状态维持需要频繁查询数据库这一慢操作而产生的,和redis不同,它将大部分(或全部)信息保存在JWT自身中,通过对JWT的解析直接获取会话的状态信息,但是这也产生了一些安全问题。
定义
Json web token (简称JWT),是目前最流行的跨域认证解决方案,是一种认证授权机制。
JWT 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
JWT组成
JWT 由三部分组成,每部分之间用点.
隔开,这三部分分别是header
、payload
、Signature
。
我们生成一个JWT来详细观察一下它的结构:JWTeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
.MZiW2KkIRI6GhKsu16Me7-3IpS4nBw1W47CW67QAqS0
正如前面说的一样,JWT分为以.
隔开的三段,并且使用了base64url
编码进行编码
base64url编码将base64中的
+
替换为-
,将/
替换为_
并且省略了=
,这样主要是为了避免歧义。
解码后的三段数据分别为:{"alg":"HS256","typ":"JWT"}
{"sub":"1234567890","name":"John Doe","iat":1516239022}
Signature
header
header
中的两个内容很好理解,一个是规定了格式为JWT
,另一个指定签名算法为HS256
,header
中通常也只有这两个字段,其中typ
不变,永远都是JWT
,alg
字段会根据所使用的签名算法不同而改变,有时还会有jwk
字段(这个也成为了一个安全问题)。
payload
payload
中包含三部分内容:标准中注册的声明、公共的声明和私有的声明。
标准中注册的声明:
iss
: jwt签发者sub
: jwt所面向的用户aud
: 接收jwt的一方exp
: jwt的过期时间,这个过期时间必须要大于签发时间nbf
: 定义在什么时间之前,该jwt都是不可用的.iat
: jwt的签发时间jti
: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
公共的声明 : 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
私有的声明 : 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
Signature
签名是JWT的最后一部分,生成方式和上面图片中蓝色字段写的一样,其中secret
字段是一个密钥,通过这个密钥进行加密后的数据再base64url
编码就得到了JWT中的最后一部分内容。
安全问题
敏感信息泄露
由于JWT的前两个字段header
和payload
并没有进行加密,所以直接使用base64url
进行解码就可以获得相应的信息,这就可能会造成敏感信息泄露,当然是否有敏感信息还需要看对应的开发者有没有往里面放就是了。
none签名方法
这个漏洞应该不会再出现了,java中有个依赖叫什么具体忘记了,其实调试过程还蛮复杂的所以就不上调试流程了。
漏洞的成因是因为在签名的生成过程中允许了使用none
作为签名算法,当签名算法为none
的时候,只需要使用base64url
编码header
和payload
并用.
分割,就可以绕过原本的签名检测,达到权限获取的目的。
签名未校验
在fusionauth-jwt 1.3.0以前,该组件即使删除了JWT
地签名部分仍然可以通过验证。
不当的错误处理
在CVE-2019-7644中,如果你发送的签名是错的,那么服务器会通知你签名错误,并返回一个正确的签名(你人还怪好的嘞)。
破解密钥
猜您是否在找:
https://github.com/hashcat/hashcat
https://github.com/Ch1ngg/JWTPyCrack
https://github.com/brendan-rius/c-jwt-cracker
更改加密算法
在CVE-2016-10555中,jwt-simple < 0.3.0版本,如果将header
中的签名算法由RS256
改为HS256
那么就会执行对称加密解密算法,将RS256
的公钥用作对称解密当中。
伪造密钥
在CVE-2018-0114中,通过前文说到的header
中的jwk
字段可以伪造公钥(自己生成的),然后用对应的私钥进行加密操作并发送,对方就会使用传输的公钥来进行解密,绕过安全认证。
硬编码
尤其是对于开源项目,如果使用了硬编码存储公钥私钥,并且开发者使用了默认的硬编码私钥,那么就会破坏原本的身份验证机制。
header中web安全问题
常见的是header
中的kid
字段:
- 由于
kid
通常用于从文件系统检索密钥文件,因此如果在使用前未对其进行过滤,可能会导致目录遍历攻击。在这种情况下,攻击者将能够指定文件系统中的任何文件作为用于验证令牌的密钥。 kid
还可用于从数据库检索密钥。在这种情况下,可以利用 SQL 注入来绕过 JWT 签名。
如果kid
参数上可以进行 SQL 注入,则攻击者可以使用此注入返回任何值。
1 |
|
例如,上面的注入将导致应用程序返回字符串key
,然后将使用字符串key
作为密钥来验证令牌。
- 如果
kid
的值被拼接到命令中用来读取文件,那么就有可能产生和web
中其他内容同样会产生的命令注入。
漏洞近况
谷歌搜索了一下感觉JWT引发的问题还是蛮多的,比如:
硬编码的CVE-2023-5074、CVE-2023-33236这两个漏洞可以通过硬编码的密钥伪造任意JWT
JWT报错产生的CVE-2023-40171和前面说的不当的错误处理
一样,当你传输了错误的内容时会返回给一个正确签名的JWT
因为验证不当导致的CVE-2023–4696属于前面说的签名未校验
类型,修改payload
中的明文信息就可以绕过权限检查
因为输入过滤不当导致的CVE-2022-23529,该漏洞允许通过JWT重写toString
方法造成RCE(不过作者本人似乎说不能RCE,只能本地利用,这里贴个图)
IAST的检测
对于使用IAST检测JWT问题想了一天也没什么通用的头绪,DongTai老大哥的代码中貌似也没看到什么有用的内容,记录下自己的思考吧。
JWT生成点
桩点:寻找代码中生成JWT的部分,通常是在用户认证成功后。
实施方案:在这些点插入代码,用以检查所使用的算法(避免None),确保密钥的强度和安全性。同时,记录生成的JWT,以便于后续审计。
JWT解析与验证点
桩点:找到解析和验证JWT的部分,通常是在API访问控制或用户会话管理中。
实施方案:在JWT解析和验证的代码处插入检测逻辑,确保签名的有效性,防止伪造或篡改。同时,检查负载中的标准声明,如过期时间(exp)、主题(sub)等。
错误处理与日志记录
桩点:在处理JWT相关异常的地方,如签名验证失败、令牌过期等。
实施方案:增强错误处理逻辑,记录所有失败的JWT验证尝试,包括令牌信息和相关的访问上下文,以便于安全分析。
敏感信息泄露检测
桩点:在JWT的payload创建过程中。
实施方案:确保JWT的负载中不含敏感信息,如用户密码、个人身份信息等。可以在这一点上插入代码,自动检测和警告任何可能的敏感信息泄露。
结语
因为只是想概括性地了解JWT的安全问题,所以对于其中的代码我也并没有进行调试,只是想给自己留个印象方便需要的时候及时回忆起来,对于JWT来说,存在的安全问题似乎和库以及组件开发者开发时的清醒程度有直接关联(这么说好像有点奇怪)。同时对于使用者来说,在JWT中存放何种信息、使用什么协议传输以及在接收时是否增加一些必要的过滤都是需要考虑的地方。
关于JWT还有很多需要学习的,看本文瞎扯一通后,希望读者对JWT安全有一个大致模糊的了解。
参考链接:
https://research.securitum.com/jwt-json-web-token-security/
https://www.authing.cn/blog/306
https://version-2.com/en/2023/01/jwt-arbitrary-command-execution-cve-2022-23529/
https://medium.com/@mnqazi/cve-2023-4696-account-takeover-due-to-improper-handling-of-jwt-tokens-in-memos-v0-13-2-13104e1412f3
https://www.cnblogs.com/tomyyyyy/p/15134420.html