深入了解Json Web Token之实战篇
本来想用python DRF 的 JWT做,后来各种失败。最终尝试了用Php,发现非常便利。
PHP 版本 PHP 7.2.4-1+b2 (cli),也就是kali linux自带的php,至于composer的安装方法,以及各个库的使用方法在此不展开,需要的话可以自己查阅官方文档
php jwt库的评测
在jwt.io上有些php jwt的库,在此说一下使用下来的感觉。只取了评分前三的库:
firebase/php-jwt Star 3786
支持PHP5/7;
操作非常简单,但是不具备很多功能,不是很推荐。
lcobucci/jwt Star Star 2729
支持PHP5/7;
不具备JWE的方法,操作简单;
不具备多重JWS,JWE方法以及其对应序列化方法。
spomky-labs/jose Star 351
仅支持 PHP 7;
功能齐全,具有多重JWE,JWS,以及其对应序列化方法。以上两个都不具备。
0×01 JWT 攻击手段
JWT 的攻击手段包括以下内容:
参考网站:https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries//。
1. 敏感信息泄露
当服务端的秘钥泄密的时候,JWT的伪造就变得非常简单容易。对此,服务端应该妥善保管好私钥,以免被他人窃取。
2. 将加密方式改为’none’
下文实战中的 Juice Shop JWT issue 1 便是这个问题。之前谈及过nonsecure JWT的问题。
签名算法确保恶意用户在传输过程中不会修改JWT。但是标题中的alg字段可以更改为none。一些JWT库支持无算法,即没有签名算法。当alg为none时,后端将不执行签名验证。将alg更改为none后,从JWT中删除签名数据(仅标题+’.'+ payload +’.')并将其提交给服务器。
解决对策:
不允许出现 none 的方法;
将开启 alg : none 作为一种额外的配置选项。
3.将算法RS256修改为HS256(非对称密码算法=>对称密码算法)
HS256使用密钥来签名和验证每个消息。而RS256使用私钥对消息进行签名并使用公钥进行认证。
如果将算法从RS256更改为HS256,则后端代码使用公钥作为密钥,然后使用HS256算法验证签名。由于攻击者有时可以获取公钥,因此攻击者可以将标头中的算法修改为HS256,然后使用RSA公钥对数据进行签名。
此时,后端代码就会使用RSA公钥+HS256算法进行签名验证,从而让验证通过。
解决对策:
不允许 HS256等对称加密 算法读取秘钥。jwtpy就是限制了这种方法。当读取到 类似于 “— xxx key —” 的参数的时候应抛出错误;
将秘钥与验证算法相互匹配。
4. HS256(对称加密)密钥破解
如果HS256密钥强度较弱,则可以直接强制使用,通过爆破 HS256的秘钥可以完成该操作。难度比较低。解决对策很简单,使用复杂的秘钥即可。
5. 错误的堆叠加密+签名验证假设
错误的堆叠加密
这种攻击发生在单个的或者嵌套的JWE中,我们想象一个JWE如下所示:
JWT RAW
header : ...
payload: "admin" : false
"uid" : 123
"umail" : 123@126.com
...
JWE Main
protected / unprotected
recipients:
en_key : key1
en_key : key2
cipher : xxx
在攻击者不修改秘钥的情况下,对于ciphertext进行修改。往往会导致解密的失败。但是,即使是失败,很多JWT的解密也是会有输出的,在没有附加认证数据(ADD)的情况下更是如此。攻击者对于ciphertext的内容进行修改,可能会让其他的数据无法解密,但是只要最后输出的payload中,有“admin”:true。 其目的就已经达到了。
解决对策:
对于JWE而言,应当解密所有数据,而非从解密的结果中提取单个需要的数据。另外,利用附加认证数据ADD,也是非常好的选择。
签名假设验证
这种攻击发生嵌套的JWS中。我们想象一个嵌套的JWS,其包括了两层的部分,其结构如下:
JWT Main
JWT Sub1
payload
Signature2
Signature
现在,攻击者通过一定的方式,能够让外层的验证通过的时候,此时,系统还应该检查内层的签名数据,如果不检查,攻击者就可以随意篡改payload的数据,来达到越权的目的。
解决对策:
因此对于嵌套JWS而言,应当验证所有层面的签名是否正确,而非验证最外层的签名是否正确就足够。
6. 无效椭圆曲线攻击
椭圆曲线加密是一种非常安全的方式,甚至从某种程度上而言,比RSA更加安全。关于椭圆曲线的算法,在此不展开。
在椭圆曲线加密中,公钥是椭圆曲线上的一个点,而私钥只是一个位于特殊但非常大的范围内的数字。 如果未验证对这些操作的输入,那攻击者就可以进行设计,从而恢复私钥。
而这种攻击已在过去中得到证实。这类攻击被称为无效曲线攻击。这种攻击比较复杂,也设计到很多的数学知识。详细可以参考文档:critical-vulnerability-uncovered-in-json-encryption。
解决对策:
检查传递给任何公共函数的所有输入是否有效是解决这类攻击的关键点。验证内容包括公钥是所选曲线的有效椭圆曲线点,以及私钥位于有效值范围内。
7. 替换攻击
在这种攻击中,攻击者需要至少获得两种不同的JWT,然后攻击者可以将令牌中的一个或者两个用在其他的地方。
在JWT中,替换共叽有两种方式,我们称他们为相同接收方攻击(跨越式JWT)和不同接收方攻击。
不同接收方攻击
我们可以设想一个业务逻辑如下:
Auth 机构,有着自己的私钥,并且给 App1 和 App2 发放了两个公钥,用于验证签名;
Attacker 利用自己的秘钥登录了 App1。
本来想用python DRF 的 JWT做,后来各种失败。最终尝试了用Php,发现非常便利。
PHP 版本 PHP 7.2.4-1+b2 (cli),也就是kali linux自带的php,至于composer的安装方法,以及各个库的使用方法在此不展开,需要的话可以自己查阅官方文档
php jwt库的评测
在jwt.io上有些php jwt的库,在此说一下使用下来的感觉。只取了评分前三的库:
firebase/php-jwt Star 3786
支持PHP5/7;
操作非常简单,但是不具备很多功能,不是很推荐。
lcobucci/jwt Star Star 2729
支持PHP5/7;
不具备JWE的方法,操作简单;
不具备多重JWS,JWE方法以及其对应序列化方法。
spomky-labs/jose Star 351
仅支持 PHP 7;
功能齐全,具有多重JWE,JWS,以及其对应序列化方法。以上两个都不具备。
0×01 JWT 攻击手段
JWT 的攻击手段包括以下内容:
参考网站:https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries//。
1. 敏感信息泄露
当服务端的秘钥泄密的时候,JWT的伪造就变得非常简单容易。对此,服务端应该妥善保管好私钥,以免被他人窃取。
内容来自无奈安全网
2. 将加密方式改为’none’
下文实战中的 Juice Shop JWT issue 1 便是这个问题。之前谈及过nonsecure JWT的问题。
签名算法确保恶意用户在传输过程中不会修改JWT。但是标题中的alg字段可以更改为none。一些JWT库支持无算法,即没有签名算法。当alg为none时,后端将不执行签名验证。将alg更改为none后,从JWT中删除签名数据(仅标题+’.'+ payload +’.')并将其提交给服务器。
解决对策:
不允许出现 none 的方法;
将开启 alg : none 作为一种额外的配置选项。
3.将算法RS256修改为HS256(非对称密码算法=>对称密码算法)
HS256使用密钥来签名和验证每个消息。而RS256使用私钥对消息进行签名并使用公钥进行认证。
如果将算法从RS256更改为HS256,则后端代码使用公钥作为密钥,然后使用HS256算法验证签名。由于攻击者有时可以获取公钥,因此攻击者可以将标头中的算法修改为HS256,然后使用RSA公钥对数据进行签名。
此时,后端代码就会使用RSA公钥+HS256算法进行签名验证,从而让验证通过。
解决对策:
不允许 HS256等对称加密 算法读取秘钥。jwtpy就是限制了这种方法。当读取到 类似于 “— xxx key —” 的参数的时候应抛出错误; 无奈人生安全网
将秘钥与验证算法相互匹配。
4. HS256(对称加密)密钥破解
如果HS256密钥强度较弱,则可以直接强制使用,通过爆破 HS256的秘钥可以完成该操作。难度比较低。解决对策很简单,使用复杂的秘钥即可。
5. 错误的堆叠加密+签名验证假设
错误的堆叠加密
这种攻击发生在单个的或者嵌套的JWE中,我们想象一个JWE如下所示:
JWT RAW
header : ...
payload: "admin" : false
"uid" : 123
"umail" : 123@126.com
...
JWE Main
protected / unprotected
recipients:
en_key : key1
en_key : key2
cipher : xxx
在攻击者不修改秘钥的情况下,对于ciphertext进行修改。往往会导致解密的失败。但是,即使是失败,很多JWT的解密也是会有输出的,在没有附加认证数据(ADD)的情况下更是如此。攻击者对于ciphertext的内容进行修改,可能会让其他的数据无法解密,但是只要最后输出的payload中,有“admin”:true。 其目的就已经达到了。
解决对策:
对于JWE而言,应当解密所有数据,而非从解密的结果中提取单个需要的数据。另外,利用附加认证数据ADD,也是非常好的选择。
签名假设验证
这种攻击发生嵌套的JWS中。我们想象一个嵌套的JWS,其包括了两层的部分,其结构如下:
JWT Main
JWT Sub1
payload
Signature2
Signature
现在,攻击者通过一定的方式,能够让外层的验证通过的时候,此时,系统还应该检查内层的签名数据,如果不检查,攻击者就可以随意篡改payload的数据,来达到越权的目的。
解决对策:
因此对于嵌套JWS而言,应当验证所有层面的签名是否正确,而非验证最外层的签名是否正确就足够。
6. 无效椭圆曲线攻击
椭圆曲线加密是一种非常安全的方式,甚至从某种程度上而言,比RSA更加安全。关于椭圆曲线的算法,在此不展开。
在椭圆曲线加密中,公钥是椭圆曲线上的一个点,而私钥只是一个位于特殊但非常大的范围内的数字。 如果未验证对这些操作的输入,那攻击者就可以进行设计,从而恢复私钥。
而这种攻击已在过去中得到证实。这类攻击被称为无效曲线攻击。这种攻击比较复杂,也设计到很多的数学知识。详细可以参考文档:critical-vulnerability-uncovered-in-json-encryption。 copyright 无奈人生
解决对策:
检查传递给任何公共函数的所有输入是否有效是解决这类攻击的关键点。验证内容包括公钥是所选曲线的有效椭圆曲线点,以及私钥位于有效值范围内。
7. 替换攻击
在这种攻击中,攻击者需要至少获得两种不同的JWT,然后攻击者可以将令牌中的一个或者两个用在其他的地方。
在JWT中,替换共叽有两种方式,我们称他们为相同接收方攻击(跨越式JWT)和不同接收方攻击。
不同接收方攻击
我们可以设想一个业务逻辑如下:
Auth 机构,有着自己的私钥,并且给 App1 和 App2 发放了两个公钥,用于验证签名;
Attacker 利用自己的秘钥登录了 App1。
本文来自无奈人生安全网