JWT原理及常見攻擊方式


JWT的全稱是Json Web Token。它遵循JSON格式,將用戶信息加密到token里,服務器不保存任何用戶信息,只保存密鑰信息,通過使用特定加密算法驗證token,通過token驗證用戶身份。基於token的身份驗證可以替代傳統的cookie+session身份驗證方法。

jwt由三個部分組成:header.payload.signature

官網:https://jwt.io/

JWT原理

0x01  JWT認證流程

在項目開發中,一般會按照上圖所示的過程進行認證,即:用戶登錄成功之后,服務端給用戶瀏覽器返回一個token,以后用戶瀏覽器要攜帶token再去向服務端發送請求,服務端校驗token的合法性,合法則給用戶看數據,否則,返回一些錯誤信息。

0x02 傳統token方式和jwt在認證方面有什么差異?

傳統token方式:

用戶登錄成功后,服務端生成一個隨機token給用戶,並且在服務端(數據庫或緩存)中保存一份token,以后用戶再來訪問時需攜帶token,服務端接收到token之后,去數據庫或緩存中進行校驗token的是否超時、是否合法。

jwt方式:

用戶登錄成功后,服務端通過jwt生成一個隨機token給用戶(服務端無需保留token),以后用戶再來訪問時需攜帶token,服務端接收到token之后,通過jwt對token進行校驗是否超時、是否合法。

0x03 JWT原理

jwt的生成token格式如下,即:由 . 連接的三段字符串組成。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

生成規則如下:

  • 第一段HEADER部分,固定包含算法和token類型,對此json進行base64url加密,這就是token的第一段。
{

   "alg": "HS256",

   "typ": "JWT"

}
  • 第二段PAYLOAD部分,包含一些數據,對此json進行base64url加密,這就是token的第二段。
{

    "sub": "1234567890",

    "name": "John Doe",

    "iat": 1516239022

     ...

}
  • 第三段SIGNATURE部分,把前兩段的base密文通過.拼接起來,然后對其進行HS256加密,再然后對hs256密文進行base64url加密,最終得到token的第三段。
base64url(
      HMACSHA256(
             base64UrlEncode(header) + "." + base64UrlEncode(payload),
             your-256-bit-secret (秘鑰加鹽)
      )
)

最后將三段字符串通過 .拼接起來就生成了jwt的token。

注意:base64url加密是先做base64加密,然后再將 - 替代 + 及 _ 替代 / 。

 

代碼實現參考:https://pythonav.com/wiki/detail/6/67/

JWT攻擊方式

加密算法

0x01 空加密算法

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

這樣的話,只要把signature設置為空(即不添加signature字段),提交到服務器,任何token都可以通過服務器的驗證。舉個例子,使用以下的字段

{
    "alg" : "None",
    "typ" : "jwt"
}
 
{
    "user" : "Admin"
}

生成的完整token為ew0KCSJhbGciIDogIk5vbmUiLA0KCSJ0eXAiIDogImp3dCINCn0.ew0KCSJ1c2VyIiA6ICJBZG1pbiINCn0

(header+'.'+payload,去掉了'.'+signature字段)

空加密算法的設計初衷是用於調試的,但是如果某天開發人員腦闊瓦特了,在生產環境中開啟了空加密算法,缺少簽名算法,jwt保證信息不被篡改的功能就失效了。攻擊者只需要把alg字段設置為None,就可以在payload中構造身份信息,偽造用戶身份。

0x02 修改RSA加密算法為HMAC

JWT中最常用的兩種算法為HMACRSA

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算法對接收到的簽名進行驗證。

參考2018CUMTCTF-Final-Web Paterbin:https://skysec.top/2018/05/19/2018CUMTCTF-Final-Web/#Pastebin/

爆破密鑰

俗話說,有密碼驗證的地方,就有會爆破。

不過對 JWT 的密鑰爆破需要在一定的前提下進行:

  • 知悉JWT使用的加密算法
  • 一段有效的、已簽名的token
  • 簽名用的密鑰不復雜(弱密鑰)

所以其實JWT 密鑰爆破的局限性很大。

相關工具:c-jwt-cracker

以下是幾個使用示例

 

可以看到簡單的字母數字組合都是可以爆破的,但是密鑰位數稍微長一點或者更復雜一點的話,爆破時間就會需要很久。

修改KID參數

kid是jwt header中的一個可選參數,全稱是key ID,它用於指定加密算法的密鑰

{
    "alg" : "HS256",
    "typ" : "jwt",
    "kid" : "/home/jwt/.ssh/pem"
}

因為該參數可以由用戶輸入,所以也可能造成一些安全問題。

0x01 任意文件讀取

kid參數用於讀取密鑰文件,但系統並不會知道用戶想要讀取的到底是不是密鑰文件,所以,如果在沒有對參數進行過濾的前提下,攻擊者是可以讀取到系統的任意文件的。

{
    "alg" : "HS256",
    "typ" : "jwt",
    "kid" : "/etc/passwd"
}

0x02 SQL注入

kid也可以從數據庫中提取數據,這時候就有可能造成SQL注入攻擊,通過構造SQL語句來獲取數據或者是繞過signature的驗證

{
    "alg" : "HS256",
    "typ" : "jwt",
    "kid" : "key11111111' || union select 'secretkey' -- "
}

0x03 命令注入

kid參數過濾不嚴也可能會出現命令注入問題,但是利用條件比較苛刻。如果服務器后端使用的是Ruby,在讀取密鑰文件時使用了open函數,通過構造參數就可能造成命令注入。

"/path/to/key_file|whoami"

對於其他的語言,例如php,如果代碼中使用的是exec或者是system來讀取密鑰文件,那么同樣也可以造成命令注入,當然這個可能性就比較小了。

修改JKU/X5U參數

JKU的全稱是"JSON Web Key Set URL",用於指定一組用於驗證令牌的密鑰的URL。類似於kidJKU也可以由用戶指定輸入數據,如果沒有經過嚴格過濾,就可以指定一組自定義的密鑰文件,並指定web應用使用該組密鑰來驗證token。

X5U則以URI的形式數允許攻擊者指定用於驗證令牌的公鑰證書或證書鏈,與JKU的攻擊利用方式類似。

信息泄露

JWT保證的是數據傳輸過程中的完整性而不是機密性。

由於payload是使用base64url編碼的,所以相當於明文傳輸,如果在payload中攜帶了敏感信息(如存放密鑰對的文件路徑),單獨對payload部分進行base64url解碼,就可以讀取到payload中攜帶的信息。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM