JWT伪造

JWT简介

JSON Web Token(JSON Web令牌)是一个开放标准(rfc7519),它定义了一种紧凑的、自包含的方式,用于在各方之间以JSON对象安全地传输信息。通过JSON形式作为Web应用中的令牌,用于在各方之间安全地将信息作为JSON对象传输。在数据传输过程中还可以完成数据加密、签名等相关处理。

传统session

HTTP协议本身是一种无状态的协议,即使用户向服务器提供了用户名和密码来进行用户认证,在下次请求时用户也得再一次进行用户认证。因为根据HTTP协议,服务器并不能知道接收到的请求来自哪个用户,所以为了让应用能识别是哪个用户发出的请求,只能在服务器存储─份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为Cookie以便下次请求时发送给应用。这样应用就能识别请求来自哪个用户。

存在问题

  1. 用户经改应用认证后,应用都要在服务端存储一份session。而session一般都是保存在内存中,随着认证用户的增多,服务端的开销会明显增大。而且用户下次的请求还必须发送到这台服务器上,这样才能拿到授权的资源。在分布式应用上会限制负载均衡器的能力。

  2. session是基于cookie来进行用户识别,cookie如果被截获,用户很容易受到CSRF(跨站伪造请求攻击)攻击。

JWT认证

认证流程:

  1. 前端通过Web表单将自己的用户名和密码发送到后端的接口。该过程一般是HTTP的POST请求。建议的方式是通过SSL加密的传输(https协议),从而避免敏感信息被嗅探。
  2. 后端核对用户名和密码成功后,将用户的id等其他信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT(Token)。
  3. 后端将JWT字符串作为登录成功的返回结果返回给前端。前端可以将返回的结果保存在localStorage(浏览器本地缓存)或sessionStorage(session缓存)上,退出登录时前端删除保存的JWT即可。
  4. 前端在每次请求时将JWT放入HTTP的Header中的Authorization位。(解决XSS和XSRF问题)
    后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确﹔检查Token是否过期;检查Token的接收方是否是自己(可选)
  5. 验证通过后后端使用JWT中包含的用户信息进行其他逻辑操作,返回相应结果

JWT结构

先放一个可以提供JWT验证的网站:https://jwt.io/

JWT是一个字符串,由三部分组成,中间用 . 隔开
例如:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphcmVuIiwiYWRtaW4iOnRydWV9.J3Vcpqx76LFtxe8xTMBORxZydb2YnPsMcSHq8cdSRww

第一部分是头部(Header),第二部分是有效载荷(Payload),第三部分是签名(Signature)
其secret为:helloctf

1、头部(Header)

头部包含两部分信息:

  • 声明类型
  • 声明加密的算法。通常直接使用HMAC、SHA256、RSA。
{
  "alg": "HS256",
  "typ": "JWT"
}

然后将头部进行base64加密,构成第一部分。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
  • 注意:可以将JWT中的alg算法修改为none:
    JWT支持将算法设定为“None”。如果“alg”字段设为“ None”,那么JWT的第三部分会被置空,这样任何token都是有效的。这样就可以伪造token进行随意访问。

2、有效载荷(Payload)

包含3部分信息:

  1. 标准中注册的声明(建议但不强制使用)

    • iss: jwt签发者
    • sub: jwt所面向的用户
    • aud: 接收jwt的一方
    • exp: jwt的过期时间,这个过期时间必须要大于签发时间
    • nbf: 定义在什么时间之前,该jwt都是不可用的.
    • iat: jwt的签发时间
    • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
  2. 公共的声明
    公共的声明可以添加任何的信息。

  3. 私有的声明
    私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。

举例一个payload:

{
  "sub": "1234567890",
  "name": "Jaren",
  "admin": true
}

然后将其进行base64加密,得到Jwt的第二部分。

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphcmVuIiwiYWRtaW4iOnRydWV9

3、签证(Signature)

包含以下三个部分:

  • base64加密后的header
  • base64加密后payload
  • 密钥secret

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

var signature = HMACSHA256(encodedString, 'secret'); //J3Vcpqx76LFtxe8xTMBORxZydb2YnPsMcSHq8cdSRww

通过JWT 进行认证

客户端接收服务器返回的JWT,将其存储在Cookie或localStorage中。此后,客户端将在与服务器交互中都会带JWT。如果将它存储在Cookie中,就可以自动发送,但是不会跨域,因此一般是将它放入HTTP请求的Header Authorization字段中。当跨域时,也可以将JWT被放置于POST请求的数据主体中。

服务器每次收到信息都会对它的前两部分进行加密,然后比对加密后的结果是否跟客户端传送过来的第三部分相同,如果相同则验证通过,否则失败。

一般是在请求头里加入Authorization,并加上Bearer标注:

fetch('api/user/1', {
  headers: {
    'Authorization': 'Bearer ' + token
  }
})

JWT本身包含认证信息,因此一旦信息泄露,任何人都可以获得令牌的所有权限。

JWT token破解绕过

空加密算法

JWT支持使用空加密算法,可以在header中指定alg为None

这样的话,只要把signature设置为空(即不添加signature字段),提交到服务器,任何token都可以通过服务器的验证。

修改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算法对接收到的签名进行验证。

爆破密钥

JWT 的密钥爆破需要在一定的前提下进行:

  • 知悉JWT使用的加密算法
  • 一段有效的、已签名的token
  • 签名用的密钥不复杂(弱密钥)

所以其实JWT 密钥爆破的局限性很大。

工具:c-jwt-cracker

文件位置/jwtcrack token

例如:
root@Jaren:~/c-jwt-cracker# /root/c-jwt-cracker/jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.sh6GvTOAjHLEnmqP_ZoUqWVtddg7KlmHqKPa9VKM5G0

然后就是漫长的等待了。。。

c-jwt-cracker配置

什么?你还要问c-jwt-cracker怎么安装?

  1. ubuntu中直接下载:git clone https://github.com/brendan-rius/c-jwt-cracker.git
  2. 安装 gcc:sudo apt install gcc
  3. 安装 make:sudo apt install make
  4. 进入c-jwt-cracker目录使用make编译:make
  5. 倘若上一步报错,使用如下命令安装该头文件:sudo apt-get install libssl-dev
  6. 正常使用即可:文件位置/jwtcrack token

JWT特殊参数的利用

修改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' -- "
}

命令注入

kid参数过滤不严也可能会出现命令注入问题,但是利用条件比较苛刻。
如果服务器后端使用的是Ruby,在读取密钥文件时使用了open函数,通过构造参数就可能造成命令注入。

"/path/to/key_file|whoami"

对于其他的语言,例如php,如果代码中使用的是exec或者是system来读取密钥文件,那么同样也可以造成命令注入。

修改JKU/X5U参数

JKU的全称是"JSON Web Key Set URL",用于指定一组用于验证令牌的密钥的URL。类似于kidJKU也可以由用户指定输入数据,如果没有经过严格过滤,就可以指定一组自定义的密钥文件,并指定web应用使用该组密钥来验证token。

X5U则以URL的形式数允许攻击者指定用于验证令牌的公钥证书证书链,与JKU的攻击利用方式类似。

参考资料

https://www.cnblogs.com/kinyoobi/p/16379076.html

https://xz.aliyun.com/t/6776?u_atoken=d0a71e9bcbd4ba5de554db9275e8cdd4&u_asig=1a0c399717314130097147500e0041

欢迎指正、交流 ~ ~ ~

作者:Jaren
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0协议
转载请注明文章地址及作者哦 ~
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇