前言
JSON Web Token(JWT)作为一种广泛使用的认证机制,因其简单、有效和灵活性而受到开发者的青睐。然而,随着JWT使用的普及,其安全性问题也逐渐显现。本文将探讨JWT的渗透测试方法,分析常见的安全漏洞,并提供相应的防护措施。
JWT的基本概念
JWT是一种用于信息交换的开放标准(RFC 7519),它以紧凑的URL安全格式编码信息。JWT通常由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。这种设计使得JWT能够在不同的系统之间安全地传递信息。
它遵循JSON格式,将用户信息加密到token里,服务器不保存任何用户信息,只保存密钥信息,通过使用特定加密算法验证token,通过token验证用户身份。基于token的身份验证可以替代传统的cookie+session身份验证方法。这使得JWT成为高度分布式网站的热门选择,在这些网站中,用户需要与多个后端服务器无缝交互。
JWT识别
1、标头(Header)
Header是JWT的第一个部分,是一个JSON对象,主要声明了JWT的签名算法,如"HS256”、"RS256"等,以及其他可选参数,如"kid"、"jku"、"x5u"等
alg字段通常用于表示加密采用的算法。如"HS256"、"RS256"等
typ字段通常用于表示类型
还有一些其他可选参数,如"kid"、"jku"、"x5u"等
2、有效载荷(Payload)
Payload是JWT的第二个部分,这是一个JSON对象,主要承载了各种声明并传递明文数据,用于存储用户的信息,如id、用户名、角色、令牌生成时间和其他自定义声明。
iss:该字段表示jwt的签发者。
sub:该jwt面向的用户。
aud:jwt的接收方。
exp:jwt的过期时间,通常来说是一个时间戳。
iat:jwt的签发时间,常来说是一个时间戳。
jti:此jwt的唯一标识。通常用于解决请求中的重放攻击。该字段在大多数地方没有被提及或使用。因为使用此字段就意味着必须要在服务器维护一张jti表, 当客户端携带jwt访问的时候需要在jti表中查找这个唯一标识是否被使用过。使用这种方式防止重放攻击似乎让jwt有点怪怪的感觉, 毕竟jwt所宣称的优点就是无状态访问
签名(Signature)
Signature是对Header和Payload进行签名,具体是用什么加密方式写在Header的alg 中。同时拥有该部分的JWT被称为JWS,也就是签了名的JWT。
对Header和Payload进行签名,具体是用什么加密方式写在Header的alg中。
同时拥有该部分的JWT被称为JWS,也就是签了名的JWT。
第一部分:对 JSON 的头部做 base64 编码处理得到
第二部分:对 JSON 类型的 payload 做 base64 编码处理得到
第三部分:分别对头部和载荷做base64编码,并使用.拼接起来
使用头部声明的加密方式,对base64编码前两部分合并的结果加盐加密处理,作为JWT
JWT安全
1、空加密算法(攻击头部不使用加密)
签名算法可被修改为none,JWT支持将算法设定为"None"。如果"alg"字段设为"None",那么签名会被置空,这样任何token都是有效的。
我们通过ctfshow靶场演示(web345)
这里可以看到我们解码后看到加密算法为None ,没有任何加密这里我们直接将jwt面向的用户改为admin即可绕过
替换后得到flag
2、未校验签名(攻击签名不使用签名认证)
算绕绕过签名(web346)
通过解码看到加密算法为HS256
我们通过插件json web tokens 直接更改加密算法为none,对象索引改为admin
如果“alg” 字段设为“ None”,那么签名会被置空,这样任何 token 都是有效的
成功绕过得到flag
3、暴力破解密钥(攻击签名知道密钥实现重组)
针对是对称加密算法(非对称没有用)
非对称要使用方法:获取源码或者公钥私钥文件
某些签名算法,例如HS256(HMAC+SHA-256),会像密码一样使用一个任意的、独立的字符串作为秘密密钥。这个秘钥如被轻易猜到或暴力破解,则攻击者能以任意的头部和载荷值来创建JWT,然后用密钥重新给令牌签名。
弱口令爆破(web347)
使用工具破解 jwt-tools
利用项目:https://github.com/ticarpi/jwt_tool
#安装必要库 python3 -m pip install pycryptodomex # 自定义修改生成 python3 jwt_tool.py JWT_HERE -T # 使用字典破解 python3 jwt_tool.py JWT_HERE -C-d dictionary.txt # 指定密码测试 python3 jwt_tool.py JWT_HERE -C-p password_here
这里我们只能弱口令top1000爆破得到密钥为123456
通过jwt官方网站替换密钥为123456后重新生成jwt提交得到flag
4、源码泄漏密匙
web(349)
该题采用RS256非对称加密,所以不能使用爆破的方式
且代码文件中泄露了公钥私钥存储位置,公钥私钥泄露,访问/private.key、/public.key
得到公钥密钥
源码中私钥生成jwt,利用公钥解密jwt,只要有私钥就可以重新生成JWT
pyhton 安装这个库
利用私钥文件重新生成
import jwt public = open('private.key', 'r').read() payload={"user":"admin"} print(jwt.encode(payload, key=public, algorithm='RS256'))
利用重新生成的jwt访问
源码了里显示提交方式是post,我们切换提交方法即可得到flag
web350(密钥混淆攻击RS256=>HS256)
这题和上题一样通过私钥生成jwt,公钥认证,但是这里我们通过看源码文件只能找到公钥文件
这里的思路就是将非对称加密改成对称加密
双方都使用公钥验签,顺利篡改数据
var jwt = require('jsonwebtoken'); var fs = require('fs'); var privateKey = fs.readFileSync('./public.key'); var token = jwt.sign({ user: 'admin' }, privateKey, { algorithm: 'HS256' }); console.log(token)