最近这次国赛遇到了json web token的题目,发现并不能很顺利的做出来,于是写下这篇学习笔记
什么是JWT
JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在两个组织之间传递安全可靠的信息。
现在网上大多数介绍JWT的文章实际介绍的都是JWS(JSON Web Signature),也往往导致了人们对于JWT的误解,但是JWT并不等于JWS,JWS只是JWT的一种实现,除了JWS外,JWE(JSON Web Encryption)也是JWT的一种实现。
JWT的组成
我们随便来看一个:
1 | eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJoZWxsbyIsImlhdCI6MTU2MjUwNDkxOCwiZXhwIjoxNTYyNTA0OTIzLCJhY2NvdW50Ijoia2sifQ.GqWnsUjzpqtE4GJrZ5Zim_nrwnQTGAlidoGqe3354yM |
JWT的格式非常简单
JWT的数据分为三个部分: headers
, payloads
,signature(签名)
三者通过.
分割,均采用base64编码
Headers
1 | eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 |
alg为算法的缩写,typ为类型的缩写,有时候还会有kid,kid表示算法所使用的密钥文件(当服务端需要多个密钥文件时使用)
Payloads
1 | eyJpc3MiOiJoZWxsbyIsImlhdCI6MTU2MjUwNDkxOCwiZXhwIjoxNTYyNTA0OTIzLCJhY2NvdW50Ijoia2sifQ |
这几个字段的含义如下,其中需要注意的字段是exp,这字段可在一定程度上被用来防止重放攻击
iss(issuer):发布者的url地址
sub(subject):该JWT所面向的用户,用于处理特定应用,不是常用的字段
aud(audience):接受者的url地址
exp(expiration):该jwt销毁的时间;unix时间戳
nbf(not before):该jwt的使用时间不能早于该时间;unix时间戳
iat(issued at):该jwt的发布时间;unix 时间戳
jti(JWT ID):该jwt的唯一ID编号
Signature
1 | GqWnsUjzpqtE4GJrZ5Zim_nrwnQTGAlidoGqe3354yM |
因为header和payload是明文存储的,所以签名是为了防止数据被修改的,提供了对数据的交易功能
签名常使用RS256(RSA 非对称加密,使用私钥签名)、HS256(HMAC SHA256 对称加密)算法,签名对象为base64encode(headers) + ‘.’ + base64encode(payloads)
攻击JWT
1. 敏感信息泄露
很明显的一点,因为payload是明文传输的,所以如果payload中存在敏感信息就会出现信息泄露。当服务端的秘钥泄密的时候,JWT的伪造就变得非常简单容易。对此,服务端应该妥善保管好私钥,以免被他人窃取。
2. 修改算法为none
签名算法保证了JWT在传输的过程中不被恶意用户修改
但是header中的alg字段可被修改为none
一些JWT库支持none算法,即没有签名算法,当alg为none时后端不会进行签名校验
将alg修改为none后,去掉JWT中的signature数据(仅剩header + ‘.’ + payload + ‘.’)然后提交到服务端即可
这种攻击的例子可以参考:http://demo.sjoerdlangkemper.nl/jwtdemo/hs256.php
代码可以在Github上找到 https://github.com/Sjord/jwtdemo/
这个例子的解法如下:
1 | # 原headers:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 |
如何把得出的值传入到可控的参数中,可以看到alg
已经被我们修改成了none
防御
不允许出现 none 的方法;
将开启 alg : none 作为一种额外的配置选项。
3. 将算法RS256修改为HS256(非对称密码算法=>对称密码算法)
算法HS256使用秘密密钥对每条消息进行签名和验证。
算法RS256使用私钥对消息进行签名,并使用公钥进行验证。
如果将算法从RS256更改为HS256,后端代码会使用公钥作为秘密密钥,然后使用HS256算法验证签名。
由于公钥有时可以被攻击者获取到,所以攻击者可以修改header中算法为HS256,然后使用RSA公钥对数据进行签名。
后端代码会使用RSA公钥+HS256算法进行签名验证。
同样的,可以通过一个例子来理解这种攻击方式 http://demo.sjoerdlangkemper.nl/jwtdemo/rs256.php
RSA公钥:http://demo.sjoerdlangkemper.nl/jwtdemo/public.pem
这个例子的解法如下:
1 | # 原headers:eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9 |
如果遇到报错
1 | jwt.exceptions.InvalidKeyError: The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret. |
解决如下:
pip3 install pyjwt==0.4.3
如何把得出的值传入到可控的参数中,可以看到我们绕过了RS256
算法,把它变成了HS256
算法,且数据可控
4. HS256(对称加密)密钥破解
如果HS256密钥强度较弱,则可以直接强制使用,通过爆破 HS256的秘钥可以完成该操作。难度比较低。
破解工具c-jwt-cracker
使用方法:
1 | ./jwtcrack eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJMM3l4IiwiaWF0IjoxNTYyNTA0OTE4LCJleHAiOjE1NjI1MDQ5MjMsImFjY291bnQiOiJrayJ9.CJcfvv6JbajrhNriA2-Hb_2yYCUy5aeAvUjVzdgBytw |
防御
使用复杂的秘钥即可