JSON Web令牌(JWT)及訪問控制繞過
JSON Web令牌是一種廣泛用于商業應用程序的訪問令牌。它們基于JSON格式,并包含一個令牌簽名以確保令牌的完整性。
本文主要討論使用JSON Web令牌(以及一般基于簽名的令牌)的安全隱患,以及攻擊者如何利用它們繞過訪問控制。
JSON Web令牌是如何工作的?
JSON Web令牌由三個組件組成:標頭(header)、 有效載荷(payload)和簽名(signature)。
標頭(header)
JSON Web令牌的標頭部分標識用于生成簽名的算法。它是JSON blob的base64url編碼字符串,如下所示:
{
"alg" : "HS256",
"typ" : "JWT"
}base64url encoded string: eyBhbGcgOiBIUzI1NiwgdHlwIDogSldUIH0K
base64url編碼是針對 URL格式的base64的修改版本。它類似于base64,但使用不同的非字母數字字符并省略填充。
最常用的算法是 HMAC和RSA算法。
有效載荷(payload)
有效載荷部分包含實際用于訪問控制的信息。在令牌中使用之前,此部分也是base64url編碼的。
{
"user_name" : "admin",
}base64url encoded string: eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg
簽名(signature)
簽名是用于驗證令牌未被篡改的部分。它的計算方法是將標頭與有效載荷串聯在一起,然后使用標頭中指定的算法進行簽名。
signature = HMAC-SHA256(base64urlEncode(header) + '.' + base64urlEncode(payload), secret_key)// Let's just say the value of secret_key is "key".-> signature function returns 4Hb/6ibbViPOzq9SJflsNGPWSk6B8F6EqVrkNjpXh7M
對于這個特定的令牌,字符串“eyBhbGcgOiBIUzI1NiwgdHlwIDogSldUIH0K.eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg”使用HS256算法和密鑰“key”進行簽名。生成的字符串是 4Hb/6ibbViPOzq9SJflsNGPWSk6B8F6EqVrkNjpXh7M。
完整的令牌
通過將每個部分(標頭、有效載荷和簽名)與每個部分之間“.”連接起來,可獲得完整的令牌,如下所示:
eyBhbGcgOiBIUzI1NiwgdHlwIDogSldUIH0K.eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg.4Hb/6ibbViPOzq9SJflsNGPWSk6B8F6EqVrkNjpXh7M
繞過JSON Web令牌控制的方法
如果正確實施,JSON Web令牌提供了一種安全的方式來識別用戶,因為有效載荷部分中包含的數據無法被篡改。由于用戶無權訪問密鑰,因此他不能自己簽署令牌。 但如果實施不當,攻擊者可以通過多種方式繞過安全機制并偽造任意令牌。
改變算法類型
攻擊者可以偽造自己的令牌的方法之一是篡改標頭的alg字段。如果應用程序不限制 JWT中使用的算法類型,攻擊者可以指定使用哪種算法,這可能會損害令牌的安全性。
1. None算法
JWT支持“None”算法。如果alg字段設置為“none”,則任何令牌在其簽名部分設置為空時都將被視為有效。例如,以下令牌將被視為有效:
eyAiYWxnIiA6ICJOb25lIiwgInR5cCIgOiAiSldUIiB9Cg.eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg.
它只是這兩個blob的base64url編碼版本,不存在簽名。
{
"alg" : "none",
"typ" : "JWT"
}{
"user" : "admin"
}
此功能最初用于調試目的。但是,如果在生產環境中將其關閉,它將允許攻擊者通過將alg字段設置為“none”來偽造他們想要的任何令牌。然后,他們可以使用偽造的令牌冒充網站上的任何人。
2. HMAC算法
用于JWT的兩種最常見的算法類型是HMAC和RSA。使用HMAC,令牌將使用密鑰簽名,然后使用相同的密鑰進行驗證。對于RSA,令牌將首先使用私鑰創建,然后使用相應的公鑰進行驗證。
HMAC -> signed with a key, verified with the same keyRSA -> signed with a private key, verified with the corresponding public key
HMAC令牌的密鑰和RSA令牌的私鑰都必須保密,因為它們用于對令牌進行簽名。
現在假設有一個應用程序最初設計為使用RSA令牌。令牌使用私鑰A簽名,該私鑰對公眾保密。然后使用公鑰B驗證令牌,任何人都可以使用該公鑰。只要令牌始終被視為RSA令牌,這就沒有問題。
Token signed with key A -> Token verified with key B (RSA scenario)
現在,如果攻擊者將alg更改為 HMAC,他可能能夠通過使用RSA公鑰B偽造的令牌進行簽名來創建有效的令牌。
這是因為本來在用RSA對令牌進行簽名的時候,程序是用RSA公鑰B驗證的,當簽名算法切換到HMAC時,還是用RSA公鑰B來驗證令牌,但是這次令牌可以使用相同的公鑰B簽名(因為它使用的是HMAC)。
Token signed with key B -> Token verified with key B (HMAC scenario)
提供無效簽名
令牌的簽名也可能在到達應用程序后從未經過驗證。這樣,攻擊者就可以通過提供無效簽名簡單地繞過安全機制。
暴力破解密鑰
也可以對用于簽署JWT的密鑰進行暴力破解。
攻擊者可以從很多信息入手,如用于對令牌進行簽名的算法、已簽名的有效載荷以及由此產生的簽名。如果用于簽署令牌的密鑰不夠復雜,則攻擊者也許可以輕松地對其進行暴力破解。
泄露密鑰
如果攻擊者無法暴力破解密鑰,則他可能會嘗試泄露密鑰。如果存在另一個漏洞(如目錄遍歷、XXE、SSRF),允許攻擊者讀取存儲密鑰值的文件,則攻擊者可以竊取密鑰并簽署任意令牌。
KID操縱
KID代表“密鑰 ID”。它是JWT中的一個可選標頭字段,它使開發人員可以指定用于驗證令牌的密鑰。KID參數的正確用法如下所示:
{
"alg" : "HS256",
"typ" : "JWT",
"kid" : "1" // use key number 1 to verify the token
}
由于這個字段是由用戶控制的,它可以被攻擊者操縱并導致危險的后果。
1. 目錄遍歷
由于KID通常用于從文件系統中檢索密鑰文件,如果在使用前沒有對其進行清理,則可能會導致目錄遍歷攻擊。在這種情況下,攻擊者將能夠指定文件系統中的任何文件作為用于驗證令牌的密鑰。
“kid”: “../../public/css/main.css” // use the publicly available file main.css to verify the token
例如,攻擊者可以強制應用程序使用公開可用的文件作為密鑰,并使用該文件簽署 HMAC令牌。
2. SQL注入
KID還可用于從數據庫中檢索密鑰。在這種情況下,可能會利用SQL注入來繞過JWT簽名。
如果KID參數上可能存在SQL注入,則攻擊者可以使用此注入返回其想要的任何值。
“kid”: "aaaaaaa' UNION SELECT 'key';--"http:// use the string "key" to verify the token
例如,上面的注入將導致應用程序返回字符串“key”(因為名為“aaaaaaa”的鍵在數據庫中不存在)。然后將使用字符串“key”作為密鑰來驗證令牌。
標頭參數操縱
除了密鑰ID,JSON Web令牌標準還使開發人員能夠通過URL指定密鑰。
1. JKU標頭參數
JKU代表“JWK設置URL”。它是一個可選的標頭字段,用于指定指向一組用于驗證令牌密鑰的URL。如果該字段被允許且未適當限制,則攻擊者可以托管自己的密鑰文件,并指定應用程序使用它來驗證令牌。
jku URL -> file containing JWK set -> JWK used to verify the token
2. JWK標頭參數
可選的JWK (JSON Web Key) 標頭參數允許攻擊者將用于驗證令牌的密鑰直接嵌入令牌中。
3. X5U、X5C URL操作
與jku和jwk標頭類似,x5u和x5c標頭參數允許攻擊者指定用于驗證令牌的公鑰證書或證書鏈。x5u以URI的形式指定信息,而x5c允許將證書值嵌入到令牌中。