JWT的结构包括三部分
头部(Header),负载(Payload) 和 签名(Signature)。它们之间使用点号“.”进行连接,构成了一个完整的JWT。
-
1. 头部(Header):通常包含两部分信息,一是指定该令牌使用的加密算法,例如HMAC SHA256或RSA;二是指定令牌的类型,即JWT。 -
2. 负载(Payload):用于存储包含用户信息、权限、过期时间等额外数据,这些数据是以JSON对象的形式存在的。负载也包含了一些预定义的标准声明,例如签发者(iss)、过期时间(exp)、主题(sub)、受众对象(aud)等。同时,负载也支持自定义声明。 -
3. 签名(Signature):用于验证JWT是否被篡改或伪造。在生成签名时,服务器会对头部和负载进行编码,并加上一个密钥进行数字签名。服务器在接收到客户端发送来的JWT后,会重新计算签名,并与原始签名进行比较以确认其有效性。
JWT 对象结构
这里可直接通过其官网进行Debugger,
官方网站:https://jwt.io/
以官网提供的例子为例,JWT
对象长这样:
一个 JWT 对象由三个部分组成,以.
进行分隔,所以上面这个JWT
对象可以分为三部分:
代码语言:javascript
复制
`Header.Payload.Signature`
-
• Header(头部) eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
-
• Payload(负载) eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
-
• Signature(签名) SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header(头部)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
是一个Base64
URL 编码后的数据,解码后得到的内容为:
Header
部分是一个JSON
对象,描述JWT
的元数据,其中有这几个参数:
-
• alg
签名使用的算法 默认为HMAC SHA256
( 简写为HS256
) -
• typ
用于标识该令牌(token
)的类型为JWT
Payload(负载)
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
同样为Base64
URL 编码后的数据。
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022}
Signature(签名)
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
此处并不是Base64
编码的数据。
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
计算过程:
首先,需要指定一个secret
,这个密钥由服务器生成,只有服务器才知道,不能泄露给用户。然后,使用Header
里面指定的签名算法(默认是HMAC SHA256
),按照下面的公式产生签名。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
针对 JWT 相关的攻击
未验证签名
未对签名进行校验,会导致攻击者随意篡改JWT的内容,从而造成越权等危害
由于后端没有对JWT
的签名进行验证,可导致Payload
字段遭到修改,以达到越权访问的目的。
修改RSA加密算法为HMAC
JWT中最常用的两种算法为HMAC
和RSA
。
HMAC
是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code)的缩写,它是一种对称加密算法,使用相同的密钥对传输信息进行加解密。
RSA
则是一种非对称加密算法,使用私钥加密明文,公钥解密密文。
在HMAC和RSA算法中,都是使用私钥对signature
字段进行签名,只有拿到了加密时使用的私钥,才有可能伪造token。
现在我们假设有这样一种情况,一个Web应用,在JWT传输过程中使用RSA算法,密钥pem
对JWT token进行签名,公钥pub
对签名进行验证。
{`` ``"alg"` `: ``"RS256"``,`` ``"typ"` `: ``"jwt"``}
通常情况下密钥pem
是无法获取到的,但是公钥pub
却可以很容易通过某些途径读取到,这时,将JWT的加密算法修改为HMAC,即
{`` ``"alg"` `: ``"HS256"``,`` ``"typ"` `: ``"jwt"``}
同时使用获取到的公钥pub
作为算法的密钥,对token进行签名,发送到服务器端。
服务器端会将RSA的公钥(pub
)视为当前算法(HMAC)的密钥,使用HS256算法对接收到的签名进行验证。
修改KID参数
kid
是jwt header中的一个可选参数,全称是key ID
,它用于指定加密算法的密钥
{`` ``"alg"` `: ``"HS256"``,`` ``"typ"` `: ``"jwt"``,`` ``"kid"` `: ``"/home/jwt/.ssh/pem"``}
因为该参数可以由用户输入,所以也可能造成一些安全问题。
任意文件读取
kid
参数用于读取密钥文件,但系统并不会知道用户想要读取的到底是不是密钥文件,所以,如果在没有对参数进行过滤的前提下,攻击者是可以读取到系统的任意文件的。
{`` ``"alg"` `: ``"HS256"``,`` ``"typ"` `: ``"jwt"``,`` ``"kid"` `: ``"/etc/passwd"``}
SQL注入
kid
也可以从数据库中提取数据,这时候就有可能造成SQL注入攻击,通过构造SQL语句来获取数据或者是绕过signature的验证
{`` ``"alg"` `: ``"HS256"``,`` ``"typ"` `: ``"jwt"``,`` ``"kid"` `: ``"key11111111' || union select 'secretkey' -- "
参考文章
https://cloud.tencent.com/developer/article/2250580
原文始发于微信公众号(无尽藏攻防实验室):JWT原理及利用
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论