?

基于SM9的JWT強身份認證方案

2024-03-25 02:05羅嬌燕左黎明陳藝琳
計算機技術與發展 2024年3期
關鍵詞:令牌數字簽名私鑰

羅嬌燕,左黎明,陳藝琳,郝 恬

(華東交通大學 理學院,江西 南昌 330013)

0 引 言

隨著互聯網應用的普及和數據交換的增加,安全問題已成為互聯網應用開發中不可忽視的挑戰[1]。傳統的基于session-cookie模式的有狀態身份認證方式不僅面臨著CSRF等安全問題,而且在可擴展性和跨域方面也存在缺陷[2]。針對上述問題,基于Token身份認證機制提出了一種無狀態、輕量級、可擴展和支持跨域的身份認證方案。根據這些優點,Token身份認證機制在API接口中得到了廣泛應用,其中以JWT技術為代表[3]。

JWT技術是由RFC 7519[4]定義的一套開放標準,具有緊湊且安全的特點,可用于執行身份認證和信息交換。但由于JWT標準的復雜性以及開發者對技術細節和加密算法的理解不夠透徹,為開發帶來便利的同時也為應用程序的安全性埋下了隱患。2015年Tim McLean[5]發現,有些類庫在實現JWT規范的過程中會產生一些漏洞;例如利用“none”算法或者直接在交互過程中刪除簽名均可以繞過簽名驗證過程。2016年,安全研究人員Sjoerd Langkemper[6]提出一種針對簽名算法的攻擊,將RSA和HMAC算法混淆使用來繞過服務器的身份認證。2021年,Apache發布的安全公告中,公開了Apache ShenYu中的身份驗證繞過漏洞(編號:CVE-2021-37580);由于產品對客戶端提交的token進行解析的同時沒有進行校驗,導致攻擊者可繞過認證,直接進入系統后臺。2022年Khaled Nassar[7]在github上披露了Oracle JavaSE數字簽名驗證機制的漏洞利用代碼(編號:CVE-2022-21449),該漏洞是由于開發人員在實現橢圓曲線數字簽名算法(ECDSA)時未充分考慮簽名驗證機制所導致的,攻擊者可以偽造簽名從而繞過身份驗證。同年,nodejs的開源基礎庫JsonWebToken被曝存在遠程代碼執行漏洞(編號:CVE-2022-23529),攻擊者可以通過該漏洞遠程執行惡意代碼,導致系統崩潰、數據泄露、賬戶被劫持等風險,從而給用戶的隱私和財產帶來巨大威脅。此外,由于該庫每月在NPM上下載量超過3 600萬次,應用于超過22 000個開源項目,其中包括微軟、Twilio、Salesforce、Intuit、Box、IBM、Docusign、Slack、SAP等知名公司創建的開源項目,這也意味著該漏洞的影響范圍極其廣泛。

JWT中存在的安全問題,例如“none”算法繞過(漏洞編號:CVE-2018-1000531、CVE-2022-23540)、敏感信息泄露(漏洞編號:CVE-2019-7644、CVE-2022-23541)、密鑰窮舉攻擊、算法混淆攻擊(漏洞編號:CVE-2016-10555)等,主要是由于開發人員對于JWT技術理解程度的不夠深入而引發的[8]?;谏鲜霭踩珕栴},該文提出了一種基于國密SM9的JWT強身份認證方案,利用國密SM9算法對認證參數進行數字簽名,實現對用戶身份的安全認證。

1 JWT身份認證及其攻擊

1.1 JWT技術

消息保護技術在應用層中很少單獨使用,通常融合在整個安全技術體系之中,而JWT就是代表性的消息保護技術[9]。JWT可有效保證多方通信中的信息安全,在無需頻繁調用資源服務器或者數據庫的情況下,JWT就可驗證后續客戶端請求,可適用于分布式站點的單點登錄場景[10-11]。該文主要基于JWT的身份認證功能進行分析和優化。圖1展示了基于JWT的身份認證流程[12]。首先,客戶端使用登錄憑證(比如用戶名和密碼)請求授權服務器;憑據驗證通過后,授權服務器利用密鑰生成一個JWT令牌返回客戶端。然后,客戶端攜帶此JWT令牌請求資源服務器上受保護的資源;資源服務器從JWT中解析用戶請求并進行處理。

圖1 JWT工作流程

JWT是一個由三部分組成的字符串,分別是頭部(Header)、載荷(Payload)和簽名(Signature),點(“.”)號作為相鄰部分的分隔符。其中頭部和載荷部分本身是JSON格式的數據,可以利用Base64url算法對內容進行解碼,解碼后的內容如下所示。

Headers = {

"typ": "JWT",

"alg": "HS256"

}

Payload = {

"iss": "Token Builder",

"iat": 1680163910,

"exp": 1680163970,

"sub": "tom@webgoat.org",

"user name": "Tom",

"Email": "tom@webgoat.org",

"admin": "true"

}

SIGNATURE =HMACSHA256{

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

密鑰

}

頭部定義令牌類型以及所采用的加密算法,HS256代表該JWT令牌的簽名算法為HMAC SHA256。載荷部分主要是對實體(通常是用戶實體)和附加數據的聲明,聲明通常分為三類,分別是注冊聲明、公共聲明和私有聲明。圖中的iss(發布者)、iat(發布時間)、exp(到期時間)、sub(主題)等字段都是屬于注冊聲明,并非強制性使用的;公共聲明和私有聲明開發者可根據實際交互情況進行設置。簽名部分是利用算法對頭部和載荷部分進行簽名,防止數據被篡改[4];在頭部聲明的簽名算法不同,簽名的過程也是不同的。

1.2 針對基于JWT身份認證的攻擊

JWT是API技術中消息傳遞的重要形式,各個API服務提供方在OAuth 2.0和OpenID Connect使用它在各方之間交換信息[9]。盡管JWT的使用范圍很廣,但常常因為提供方的實現不規范產生了一些安全問題。JWT由三部分組成,該文針對各部分產生的漏洞進行分析,并對一些典型漏洞進行研究。

(1)針對頭部的攻擊。

“none”算法繞過。

JWT的頭部通過設置typ參數和alg參數分別標明了Token類型和簽名算法。在RFC 7518[13]標準中,定義了JWT可以使用一系列算法進行簽名,也可以不使用算法進行簽名,即將alg參數的值設置為none。如圖2所示,在JWT中設置“none”算法時,在簽名部分將不使用任何算法進行簽名,即簽名部分為空字符串,后端服務器不執行簽名驗證,此時前兩部分符合規范的任何token都是有效的?!皀one”算法最初的目的是為了方便開發者在開發環境中進行調試,如果在生產環境中也使用該功能,攻擊者便可以偽造任意用戶的token進行身份認證[5]。

圖2 基于“none”算法生成的JWT

(2)針對載荷的攻擊。

當JWT用于身份認證時,通常需要包含用戶的相關信息,而載荷部分便是用于聲明用戶實體及其要發送的數據信息,是JWT不可缺少的一部分[4]。根據身份認證所需要的信息生成一個JSON對象,利用Base64url算法對其進行編碼,編碼后的字符串便是JWT的第二部分。按照RFC4648[14]的定義,Base64url算法是基于Base64算法而形成的一種加密方式,對內容進行了簡單編碼,無法保證傳輸過程中信息的機密性。因此可以直接利用Base64url算法對JWT中的載荷部分進行解碼,如圖3所示,從解碼后的內容可以獲取身份認證過程所包含的敏感信息。通過載荷部分泄露的敏感信息,推斷身份認證的參數及其含義,再結合前面提到的“none”算法漏洞,攻擊者可以直接接管賬號或提取權限[9]。

圖3 基于載荷部分的敏感信息泄露漏洞

(3)針對簽名的攻擊。

①算法混淆攻擊。

簽名部分主要是根據頭部的alg參數設置的算法類型對Base64url算法編碼后的頭部和載荷兩部分內容進行簽名,從而保證了JWT在數據傳輸過程的不可篡改性。RFC 7518[13]標準聲明了對稱算法和非對稱算法均可以作為JWT的簽名算法,由于有些開發者的技術實現不規范,提供了與算法無關的簽名驗證方法verify()。算法偽碼如下:該方法只是根據頭部(Header)的alg參數值確定簽名的算法類型,而不驗證提供的Token加密算法,將導致身份認證服務器驗證JWT簽名的算法與開發人員預期的算法不相同,從而產生對稱和非對稱算法混淆漏洞。

publicKey = ;

token = request.getCookie("session");

verify(token, publicKey);

……

function verify(token, secretOrPublicKey){

algorithm = token.getAlgHeader();

if(algorithm == "RS256"){

// Use the provided key as an RSA public key

} else if (algorithm == "HS256"){

// Use the provided key as an HMAC secret key

} }

產生算法混淆漏洞的流程如圖4所示。當開發人員調用verify()方法但僅使用非對稱算法(如RS256)作為JWT的簽名算法,即在身份認證過程中,認證服務器使用私鑰對數據簽名,使用公鑰進行簽名驗證[15]。由于公鑰對所有人可見,攻擊者獲取RS256公鑰并將其作為HS256算法的簽名密鑰,同時將頭部的alg參數值設為“HS256”,然后對修改后頭部和載荷部分結合HS256算法進行簽名生成JWT令牌。在身份認證過程中,認證服務器將使用HS256算法和相同的公鑰去驗證簽名。

圖4 基于簽名部分的算法混淆漏洞流程

②密鑰窮舉攻擊。

密鑰窮舉攻擊(Brute Force Attack)是通過暴力破解的方式對所有可能的密鑰值進行嘗試來達到破解加密數據的目的的一種攻擊方式。當開發者在使用某些簽名算法(比如HS256)時,可將任意長度的字符串作為簽名密鑰生成簽名,此時若使用一個長度較短的弱密鑰,將會導致密鑰窮舉攻擊。圖5為利用python暴力破解腳本破解弱密鑰簽名的JWT令牌的運行結果。

圖5 python破解弱密鑰簽名的JWT令牌

2 基于國密SM9的JWT強身份認證方案

2.1 國密SM9算法簡介

SM9標識密碼算法[16]是利用橢圓曲線對實現的基于標識的密碼算法,算法的安全性主要是建立在有關雙線性對問題難解性的基礎之上。SM9算法主要由三部分組成,分別是數字簽名算法、密鑰交換協議、密鑰封裝機制和公鑰加密算法[17]。該文主要使用SM9算法中的數字簽名部分,主要分為三個步驟:密鑰產生、生成簽名和簽名驗證。

參數說明見文獻[17],下面主要描述SM9數字簽名算法的各個步驟。

(1)密鑰產生。

KGC(Key Generation Center,密鑰生成中心)[16]產生隨機數ks∈[1,N-1]作為簽名主私鑰,Ppub=[ks]P2作為簽名主公鑰,則簽名主密鑰對為(s,Ppub),Ppub公開,ks由KGC保存。而用戶簽名密鑰對為(Ppub-A,dA),其中用戶簽名公鑰(Ppub-A=[H1(IDA‖hid)]P+Ppub,用戶簽名私鑰dA=[s·(H1(IDA‖hid)+s)-1]P1.

(2)生成簽名。

授權服務器根據用戶A的簽名私鑰dA運用SM9數字簽名算法對消息M進行計算,生成數字簽名(h,S),簽名生成流程如表1所示。

表1 生成數字簽名的算法流程

(3)簽名驗證。

資源服務器根據SM9數字簽名算法對用戶A發送的消息M'和數字簽名(h',S')進行驗證,簽名驗證過程中需要進行的步驟如表2所示。

表2 驗證數字簽名的算法流程

2.2 基于SM9的JWT強身份認證方案的令牌生成過程

基于SM9的JWT強身份認證方案提出了一種與傳統的JWT技術不同的令牌生成方式,生成過程如圖6所示。頭部(Header)和載荷(Payload)部分內容分別為Cheader和Cpayload,從Payload中獲取發行時間Valiat和用戶Alice的身份標識UidAlice,將UidAlice作為國密SM9數字簽名算法的用戶標識,Valiat作為新鮮因子。

圖6 JWT令牌生成過程

(1)通過國密SM3密碼雜湊算法Hsm3將頭部和載荷部分均映射成長度為M的數字串,即分別計算Sh=Hsm3(Cheader,Valiat)和Sp=Hsm3(Cpayload,Valiat);

(2)計算消息Msg=Sh⊕Sp;

(3)利用國密SM9數字簽名算法對Msg進行簽名,運算結果Valsig作為令牌JwtToken的簽名部分;

(4)將Cheader和Cpayload依次作為自變量x代入公式y=fbase64(x)進行計算,其中x={Cheader,Cpayload},運算結果為Valheader和Valpayload;

(5)最終,將三部分結合起來得到JWT令牌,即JwtToken=Valheader+Valpayload+Valsig。

2.3 強身份認證方案工作原理

基于JWT的身份認證方案可應用的場景有Web應用[18]、s桌面應用[19]、移動應用[19]和嵌入式應用[20],該文提出的基于SM9的JWT強身份認證方案也可以在這些場景中實現對用戶的身份認證。身份認證流程如圖7所示,主要包括用戶Alice、客戶端C、授權服務器Sa和資源服務器Sr四個節點;KGC生成Sa的簽名主私鑰ks和簽名主公鑰Ppub,Sa保存主私鑰ks,公開主公鑰Ppub。

圖7 基于JWT的強身份認證方案工作流程

(1)用戶Alice訪問客戶端C。

(2)Alice通過客戶端C向授權服務器Sa提交用戶憑據TAlice。

(3)授權服務器Sa驗證憑據TAlice,驗證通過后,生成成功狀態碼,并利用主私鑰ks和身份標識UidAlice生成簽名私鑰dAlice,然后結合國密SM9算法生成令牌JwtToken;若驗證不通過,則授權服務器Sa將會生成失敗狀態碼。

(4)授權服務器Sa將狀態碼和JWT令牌返回給客戶端C。

(5)客戶端C攜帶JWT令牌向資源服務器Sr請求相關資源。

(6)資源服務器Sr根據主公鑰Ppub和身份標識UidAlice生成Alice的簽名公鑰Ppub-Alice,利用Ppub-Alice驗證令牌JwtToken。

(7)若資源服務器Sr驗證通過,則處理請求并返回對應的服務器資源信息;反之,則返回處理失敗狀態碼。

3 安全性分析與比較

3.1 數據完整性

授權服務器對客戶端發送的用戶憑據驗證通過后,利用國密SM9數字簽名算法生成令牌JwtToken的簽名部分。如果在交互過程中,攻擊者截獲數據包,并對令牌JwtToken內容進行了篡改,那么相對應的簽名部分也會發生變化,從而無法驗證簽名。因此,該方案可以保證數據完整性,防止數據在傳輸過程中被篡改。

3.2 不可抵賴性

授權服務器首先對令牌JwtToken的前兩部分進行運算,然后利用私鑰對運算結果進行簽名生成第三部分??蛻舳丝衫檬跈喾掌靼l布的公鑰對簽名進行驗證,從而確定簽名來源的正確性;同時資源服務器也可利用公鑰對令牌JwtToken進行驗證,驗證通過后再對用戶請求進行處理。因此,該方案可以實現授權服務器的不可抵賴性,從而保證了數據的可靠性和真實性。

3.3 抗重放攻擊

重放攻擊(Replay Attacks)[21]又稱回放攻擊,指的是攻擊者發送一個服務器已經接收過的包,從而實現欺騙服務器的目的。該方案選取時間戳作為新鮮因子嵌入在待簽名的消息中,服務器在驗證簽名時,首先會驗證令牌JwtToken的新鮮性,如果請求的時間不在有效時間范圍內,即令牌JwtToken已經失效了,將會跳轉到登錄頁面,重放數據包如圖8所示。加入時間戳作為新鮮因子,有效預防了重放攻擊,保證了數據的唯一性和失效性。

圖8 抗重放攻擊測試數據包

3.4 可抵抗針對頭部(Header)的“none”算法繞過攻擊

該文在前面提到將頭部alg參數設置為“none”時,后端服務器將不執行驗證簽名的過程,從而導致客戶端攜帶無簽名的Token也可進行資源訪問。該文提出的認證方案中簽名認證過程是基于代碼中定義的國密SM9算法進行認證,與alg參數無關;同時當alg參數值不等于SM9時,令牌JwtToken將會失效。圖9為模擬客戶端攜帶“none”算法的JWT向服務器請求資源的過程。

圖9 可抵抗針對頭部(Header)的“none”算法繞過攻擊測試數據包

3.5 可抵抗針對載荷(Payload)的敏感信息泄露攻擊

JWT在載荷(Payload)部分存放于用戶實體的相關信息進行身份認證[4],如果開發者在聲明用戶實體的過程中直接明文存儲相關信息,將會導致敏感信息泄露攻擊。該文提出的認證方案針對載荷(Payload)部分所需要聲明的用戶實體信息均進行了編碼操作,并且不將用戶敏感信息存放在令牌JwtToken中,保證了用戶信息的安全性,防止敏感信息泄露漏洞的出現。圖10為模擬攻擊者截獲令牌JwtToken后解密的內容。

圖10 解密令牌JwtToken

3.6 可抵抗針對簽名(Signature)的算法混淆攻擊

該文提出的認證方案中授權服務器利用私鑰生成簽名,客戶端和資源服務器可以利用公鑰進行簽名驗證,并且簽名的生成和驗證過程都是基于國密SM9算法,與alg參數無關。不過當alg參數值不等于SM9時,令牌JwtToken將被判定為無效令牌。

3.7 可抵抗針對簽名(Signature)的密鑰窮舉攻擊

該文提出的認證方案中的簽名是國密SM9算法生成的,該算法是用橢圓曲線實現的基于標識的數字簽名算法,具有很高的安全性和抗暴力破解能力。并且用戶私鑰是根據大整數類型的主私鑰和用戶標識生成的,假設攻擊者獲得了用戶標識,也不可能通過猜測的方式獲取用戶私鑰,從而無法破解加密數據,預防了密鑰窮舉攻擊。

4 實驗仿真與安全性比較

4.1 安全性比較

選用國內外應用了JWT技術實現了身份驗證和授權的平臺[22-23]進行安全性分析與對比,分析結果如表3所示。

表3 安全性對比分析

方案1:Microsoft Azure Active Directory;

方案2:百度數據開放平臺;

方案3:文中方案。

4.2 方案實現與仿真

該文在Windows 10 64位操作系統下,使用基于 JDK 1.8的環境和IntelliJ IDEA 2021.2.1開發平臺,實現了一個基于B/S架構的身份認證方案,其中服務器端采用了 SpringMVC和Mybatis框架進行開發,方案的核心代碼如下:

服務器端根據用戶Alice身份標識生成數字簽名:

// 用戶標識

String id_A = userId;

//初始化Header和Payload部分

String orgHeader = headerJson.to String();

String orgPayload = bodyJson.to String();

// 生成待簽名的消息msg

String msg = getMsg(orgHeader,orgPayload,iatBytes);

// Header和Payload分別進行base64url編碼

String header = base64URLEn(orgHeader);

String payload = base64URLEn(orgPayload);

// 對消息msg進行簽名

String signatrue = sm9JwtUtils.sm9_sign(kgc,sm9,P_pub,ks,id_A,msg);

// 生成最終的JWT

String JwtToken = joinWithDot(header,payload,signatrue);

服務器端驗證簽名:

// JWT令牌解析

String header = base64URLDe(tokenParts[0]);

String payload = base64URLDe(tokenParts[1]);String signature = tokenParts[2];

// 解析payload部分

JSONObject payloadJson = new JSONObject(payload);

// 獲取用戶標識

String id_A = payloadJson.get("useId").to String();

// 獲取 iat 字段的值,并轉換為字節數組

byte[ ] iatBytes = ByteBuffer.allocate(Long.BYTES).putLong(payloadJson.optLong("iat")).array();

// 生成消息msg

String msg = getMsg(header,payload,iatBytes);

//驗證簽名

boolean flag = sm9JwtUtils.sm9_verify(kgc,sm9,P_pub,ks,id_A,msg,signature);

(1)用戶Alice在客戶端通過登錄頁面向授權服務器端提交用戶憑據,驗證通過后,授權服務器端根據用戶標識UidAlice和主私鑰ks生成令牌JwtToken,并返回給客戶端,數據包如圖11所示。

圖11 服務器端將JWT令牌給客戶端的數據包

(2)客戶端攜帶令牌JwtToken向資源服務器請求資源,資源服務器解析傳遞令牌JwtToken,獲取用戶標識UidAlice,并計算消息Msg,然后再根據授權服務器公開的主公鑰Ppub和UidAlice驗證令牌JwtToken中的簽名部分Valsig。如果驗證通過,資源服務器將根據UidAlice處理客戶端請求,并返回對應的資源,資源請求交互過程的數據包如圖12所示。

圖12 資源請求交互過程

5 結束語

針對JWT技術中存在的一些漏洞,該文提出了一種基于國密SM9算法的JWT強身份認證方案。與現有開放平臺所使用的JWT技術進行安全性分析與比較,可知該認證方案具有數據完整性、不可抵賴性和抗重放攻擊等特點,并且可抵抗“none”算法繞過、敏感信息泄露、算法混淆和密鑰窮舉等攻擊。但實驗表明該方案的運行效率不高,有待進一步完善。

猜你喜歡
令牌數字簽名私鑰
清掃機器人避障系統區塊鏈私鑰分片存儲方法
稱金塊
比特幣的安全性到底有多高
基于改進ECC 算法的網絡信息私鑰變換優化方法
淺析計算機安全防護中數字簽名技術的應用
基于路由和QoS令牌桶的集中式限速網關
動態令牌分配的TCSN多級令牌桶流量監管算法
一種基于虛擬私鑰的OpenSSL與CSP交互方案
基于數字簽名的QR碼水印認證系統
數字簽名簡述
91香蕉高清国产线观看免费-97夜夜澡人人爽人人喊a-99久久久无码国产精品9-国产亚洲日韩欧美综合