JWT 漏洞
- 在用户注册或登录后,我们想记录用户的登录状态,或者为用户创建身份认证的凭证。我们不再使用 Session 认证机制,而使用 Json Web Token 认证机制。
1 什么是 JWT
- Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准((RFC 7519). 该 token 被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。
- JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 token 也可直接被用于认证,也可被加密。
token 的认证和传统的 session 认证的区别
传统的 session 认证:
- 我们知道,http 协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据 http 协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为 cookie, 以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了 , 这就是传统的基于 session 认证。
- 但是这种基于 session 的认证使应用本身很难得到扩展,随着不同客户端用户的增加,独立的服务器已无法承载更多的用户,而这时候基于 session 认证应用的问题就会暴露出来。
基于 session 认证所显露的问题:
- Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言 session 都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
- 扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上 , 这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。
- CSRF: 因为是基于 cookie 来进行用户识别的, cookie 如果被截获,用户就会很容易受到跨站请求伪造的攻击。
- 基于 token 的鉴权机制:基于 token 的鉴权机制类似于 http 协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于 token 认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。
2 JWT 认证过程
传统的 session 流程
- 浏览器发起请求登陆
- 服务端验证身份,生成身份验证信息,存储在服务端,并且告诉浏览器写入 Cookie
- 浏览器发起请求获取用户资料,此时 Cookie 内容也跟随这发送到服务器
- 服务器发现 Cookie 中有身份信息,验明正身
- 服务器返回该用户的用户资料
JWT 流程
- 浏览器发起请求登陆
- 服务端验证身份,根据算法,将用户标识符打包生成 token, 并且返回给浏览器
- 浏览器发起请求获取用户资料,把刚刚拿到的 token 一起发送给服务器
- 服务器发现数据中有 token,验明正身
- 服务器返回该用户的用户资料
3 JWT 格式
JWT 是由三段信息构成的,将这三段信息文本用.链接一起就构成了 Jwt 字符串。就像这样:
eyJraWQiOiI5MTM2ZGRiMy1jYjBhLTRhMTktYTA3ZS1lYWRmNWE0NGM4YjUiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTY0ODAzNzE2NCwibmFtZSI6IkNhcmxvcyBNb250b3lhIiwic3ViIjoiY2FybG9zIiwicm9sZSI6ImJsb2dfYXV0aG9yIiwiZW1haWwiOiJjYXJsb3NAY2FybG9zLW1vbnRveWEubmV0IiwiaWF0IjoxNTE2MjM5MDIyfQ.SYZBPIBg2CRjXAJ8vCER0LA_ENjII1JakvNQoP-Hw6GG1zfl4JyngsZReIfqRvIAEi5L4HV0q7_9qGhQZvy9ZdxEJbwTxRs_6Lb-fZTDpW6lKYNdMyjw45_alSCZ1fypsMWz_2mTpQzil0lOtps5Ei_z7mM7M8gCwe_AGpI53JxduQOaB5HkT5gVrv9cKu9CsW5MS6ZbqYXpGyOG5ehoxqm8DL5tFYaW3lB50ELxi0KsuTKEbD0t5BCl0aCR2MBJWAbN-xeLwEenaqBiwPVvKixYleeDQiBEIylFdNNIMviKRgXiYuAvMziVPbwSgkZVHeEdF5MQP1Oe2Spac-6IfA
复制
- 第一部分我们称它为头部(header)
- 第二部分我们称其为载荷(payload)
- 第三部分是签证(signature)
header 部分承载两部分信息:
- 声明类型,这里是 jwt
- 声明加密的算法 通常直接使用 HMAC SHA256
- 完整的头部就像下面这样的 JSON:
{"kid":"9136ddb3-cb0a-4a19-a07e-eadf5a44c8b5","alg":"RS256"}
JSON复制
- 将 header 进行 base64 加密(该加密是可以对称解密的 ), 构成了第一部分。
payload 是存放有效信息的地方,这些有效信息包含三个部分:
- 标准中注册的声明
- 公共的声明
- 私有的声明
标准中注册的声明 (建议但不强制使用) :
iss: jwt 签发者。
sub: jwt 所面向的用户。
aud: 接收 jwt 的一方。
exp: jwt 的过期时间,这个过期时间必须要大于签发时间。
nbf: 定义在什么时间之前,该 jwt 都是不可用的。
iat: jwt 的签发时间。
jti: jwt 的唯一身份标识,主要用来作为一次性 token, 从而回避重放攻击。
Bash复制
公共的声明 :
- 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密。
私有的声明 :
- 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为 base64 是对称解密的,意味着该部分信息可以归类为明文信息。
定义一个 payload:
{"iss":"portswigger","exp":1648037164,"name":"Carlos Montoya","sub":"carlos","role":"blog_author","email":"[email protected]","iat":1516239022}
JSON复制
- 将 payload 进行 base64 加密,构成了第二部分。
signature 是一个签证信息,这个签证信息由三部分组成:
- header (base64 后的)
- payload (base64 后的)
- secret
- 这个部分需要 base64 加密后的 header 和 base64 加密后的 payload 使用.连接组成的字符串,然后通过 header 中声明的加密方式进行加盐 secret 组合加密,然后就构成了 jwt 的第三部分。
发出令牌的服务器通常通过哈希头和有效负载来生成签名。在某些情况下,它们还对生成的哈希进行加密。
无论哪种方式,此过程都涉及一个秘密签名密钥。此机制为服务器提供了一种方法,用于验证令牌内的数据自发布以来未被篡改:
由于签名直接来自令牌的其余部分,因此更改头或负载的单个字节会导致签名不匹配。
如果不知道服务器的秘密签名密钥,就不可能为给定的头或负载生成正确的签名。
4 jwt 的转换
我们可以通过 jwt.io(https://jwt.io/) 进行 jwt 的编码转换
而当我们将生成的 JWT 重新复制回页面后,会发现由于 secret 的错误导致 invalid signature。
或者我们可以这样理解 JWT:
- JWT 相当于一张电影票
- 电影院的工作人员会通过电影票的防伪措施进行判定,如果为真则可以看电影:)
- 而站在攻击者的角度则会变成如果进行高仿真的造价,亦或者说能否搞到打印电影票的打印机。
附录:可以使用 jwt 的命令:
apt-get install jwt
jwt -show aaa
5 Brute-forcing secret keys
我们可以通过 hastcat 工具针对 JWT 的 secret key 进行暴力破解。
我们可以通过如下的命令方式进行破解:
hashcat -a 0 -m 16500 <jwt.txt> <wordlist>
- 测试成功后,可以通过 –show 的参数进行展示
测试的 key 内容如下:
- eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmYXJtc2VjIiwibmFtZSI6IlRRIiwiaWF0IjoxODg4ODg4ODg4OH0.NBlmhXhuEj64iLOb9dCKozDcZYYpcMJrsdDUVjadAEg
- hashcat -a 0 -m 16500 jwt.txt pass.txt
- hashcat -a 0 -m 16500 jwt.txt pass.txt --show
现教案有个 bug 待解决:
- 当通过 jwt.io 进行编码时,如果选择 secret base64 encoded,那么爆破不成功。
现未确定问题出在哪?学会了教我~
6 jwt 的靶场
靶场一:burpsuite
靶场二:webgoat
为了方便,这里直接用 docker 来做测试
docker search webgoat
docker pull webgoat/webgoat-8.0:v8.1.0
docker pull webgoat/webwolf:v8.1.0
docker pull webgoat/goatandwolf:v8.1.0
docker images
docker run -d -p 8888:8888 -p 8080:8080 -p 9090:9090 webgoat/goatandwolf:v8.1.0
暂无评论内容