JWT 組成詳解

編者注:本文譯自 Fusion Auth Developer[1]。JSON Web Token(通常縮寫爲 JWT)是一種通常與 OAuth2 等標準協議一起使用的令牌。本文解釋了 JWT 的組成部分和工作原理。

在我們繼續之前,重要的是要注意 JWT 通常被錯誤地稱爲 JWT Tokens。在末尾添加 Token 將會使其變成 JSON Web Token Token。因此,在本文中,我們省略末尾的 Token 並簡單地稱之爲 JWT,因爲這是更正確的名稱。同樣地,由於 JWT 通常用作身份驗證和授權過程的一部分,一些人將其稱爲 Authentication Tokens 或 JWT Authentication Tokens。從技術上講,JWT 只是一個包含 Base64 編碼的 JSON 的令牌。它可以用於許多不同的用例,包括身份驗證和授權。因此,在本文中,我們不使用這個術語,而是討論如何在身份驗證過程中使用 JWT。

讓我們開始吧!這是一個新生成的 JWT。爲清楚起見添加了換行符,但它們通常不存在。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImY1ODg5MGQxOSJ9.eyJhdWQiO
iI4NWEwMzg2Ny1kY2NmLTQ4ODItYWRkZS0xYTc5YWVlYzUwZGYiLCJleHAiOjE2NDQ4ODQ
xODUsImlhdCI6MTY0NDg4MDU4NSwiaXNzIjoiYWNtZS5jb20iLCJzdWIiOiIwMDAwMDAwM
C0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDEiLCJqdGkiOiIzZGQ2NDM0ZC03OWE5LTR
kMTUtOThiNS03YjUxZGJiMmNkMzEiLCJhdXRoZW50aWNhdGlvblR5cGUiOiJQQVNTV09SR
CIsImVtYWlsIjoiYWRtaW5AZnVzaW9uYXV0aC5pbyIsImVtYWlsX3ZlcmlmaWVkIjp0cnV
lLCJhcHBsaWNhdGlvbklkIjoiODVhMDM4NjctZGNjZi00ODgyLWFkZGUtMWE3OWFlZWM1M
GRmIiwicm9sZXMiOlsiY2VvIl19.dee-Ke6RzR0G9avaLNRZf1GUCDfe8Zbk9L2c7yaqKME

這可能看起來像是一堆亂碼,但隨着您對 JWT 以及它們在 OAuth2 或身份驗證過程中的使用方式瞭解得更多,它開始變得更有意義了。

有幾種類型的 JSON Web 令牌,但我將重點介紹已簽名的 JWT,因爲它們是最常見的。簽名的 JWT 也可以稱爲 JWS。它由三個部分組成,以句號分隔。

有一個標頭,在上面的 JWT 中以 eyJhbGc 開頭。然後有一個主體或有效載荷,上面以 eyJhdWQ 開頭。最後有一個簽名,在示例 JWT 中以 dee-K 開頭。

JWT 如何工作?讓我們拆開這個示例 JWT 並深入瞭解一下。

JWT 標頭解釋

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImY1ODg5MGQxOSJ9 是 JWT 的標頭。JWT 標頭包含有關 JWT 的元數據,包括密鑰標識符、用於登錄的算法和其他信息。

如果您通過 base64 解碼器運行上述標頭:

echo 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImY1ODg5MGQxOSJ9'|base64 -d

你會看到這個 JSON:

{"alg":"HS256","typ":"JWT","kid":"f58890d19"}%

HS256 表示 JWT 是使用對稱算法簽名的,特別是使用 SHA-256 的 HMAC。

算法列表和實現支持級別如下。

XqhfMo

此表取自 RFC 7518。由於僅 HS256 需要符合規範,請查閱用於創建 JWT 的軟件或庫以獲取有關受支持算法的詳細信息。

其他元數據也存儲在 JWT 的這一部分中。typ 標頭指示 JWT 的類型。在本例中,該值爲 JWT,但其他值均有效。例如,如果 JWT 符合 RFC 9068,它可能具有 at+JWT 指示它是訪問令牌的值。

該 kid 值指示用於簽署 JWT 的密鑰。對於對稱密鑰,kid 可用於在祕密保險庫中查找值。對於非對稱簽名算法,此值讓 JWT 的消費者查找與簽署此 JWT 的私鑰相對應的正確公鑰。正確處理此值對於簽名驗證和 JWT 負載的完整性至關重要。

通常情況下,將標頭值的大部分處理過程轉移到庫中。有許多優秀的開源 JWT 處理庫。您應該瞭解這些庫的價值,但可能不必實現實際處理。

JWT 令牌主體

有效載荷或主體是使 JWT 變得有趣的地方。此部分包含創建 JWT 以傳輸的數據。例如,如果 JWT 表示授權訪問某些數據或功能的用戶,則有效載荷包含用戶數據,例如角色或其他授權信息。

這是來自示例 JWT 的有效負載:

eyJhdWQiOiI4NWEwMzg2Ny1kY2NmLTQ4ODItYWRkZS0xYTc5YWVlYzUwZGYiLCJleHAiOjE2NDQ4ODQxODUsImlhdCI6MTY0NDg4MDU4NSwiaXNzIjoiYWNtZS5jb20iLCJzdWIiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDEiLCJqdGkiOiIzZGQ2NDM0ZC03OWE5LTRkMTUtOThiNS03YjUxZGJiMmNkMzEiLCJhdXRoZW50aWNhdGlvblR5cGUiOiJQQVNTV09SRCIsImVtYWlsIjoiYWRtaW5AZnVzaW9uYXV0aC5pbyIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJhcHBsaWNhdGlvbklkIjoiODVhMDM4NjctZGNjZi00ODgyLWFkZGUtMWE3OWFlZWM1MGRmIiwicm9sZXMiOlsiY2VvIl19

如果您通過 base64 解碼器運行示例負載:

echo 'eyJhdWQiOiI4NWEwMzg2Ny1kY2NmLTQ4ODItYWRkZS0xYTc5YWVlYzUwZGYiLCJleHAiOjE2NDQ4ODQxODUsImlhdCI6MTY0NDg4MDU4NSwiaXNzIjoiYWNtZS5jb20iLCJzdWIiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDEiLCJqdGkiOiIzZGQ2NDM0ZC03OWE5LTRkMTUtOThiNS03YjUxZGJiMmNkMzEiLCJhdXRoZW50aWNhdGlvblR5cGUiOiJQQVNTV09SRCIsImVtYWlsIjoiYWRtaW5AZnVzaW9uYXV0aC5pbyIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJhcHBsaWNhdGlvbklkIjoiODVhMDM4NjctZGNjZi00ODgyLWFkZGUtMWE3OWFlZWM1MGRmIiwicm9sZXMiOlsiY2VvIl19' |base64 -d

你會看到這個 JSON:

{
  "aud""85a03867-dccf-4882-adde-1a79aeec50df",
  "exp": 1644884185,
  "iat": 1644880585,
  "iss""acme.com",
  "sub""00000000-0000-0000-0000-000000000001",
  "jti""3dd6434d-79a9-4d15-98b5-7b51dbb2cd31",
  "authenticationType""PASSWORD",
  "email""admin@fusionauth.io",
  "email_verified": true,
  "applicationId""85a03867-dccf-4882-adde-1a79aeec50df",
  "roles"[
    "ceo"
  ]
}

請注意,創建簽名 JWT 的算法可以刪除 base64 填充,因此 JWT 末尾可能缺少符號。您可能需要將其添加回去才能解碼 JWT 令牌。這取決於內容的長度。您可以在此處瞭解更多信息 [2]。

如上所述,負載是您的應用程序所關心的,所以讓我們更仔細地看一下這個 JSON。對象的每個鍵都稱爲 “聲明”(Claim)。

一些聲明是衆所周知的,其含義由 IETF 等標準機構規定。您可以在此處查看此類聲明的示例 [3]。這些包括示例令牌中的 iss 和 aud 聲明。當它們出現在 JWT 的有效負載中時,這兩者都具有定義的含義。

還有其他非標準聲明,例如 authenticationType。這些聲明可能代表業務領域或自定義數據。例如,authenticationType 是 FusionAuth 使用的專有聲明,用於指示身份驗證方法,例如密碼、刷新令牌或通過無密碼鏈接。

您可以向 JWT 添加您想要的任何聲明,包括對 JWT 的下游消費者有用的數據。從 roles 聲明中可以看出,聲明不必是簡單的 JSON 原語。它們可以是任何可以用 JSON 表示的數據結構。

聲明驗證

當代碼與 JWT 一起出現時,它應該驗證某些聲明。至少,應檢查這些聲明:

除了這些之外,還要驗證業務領域特定的聲明。例如,使用上述 JWT 的人可以在 authenticationType 未知值時拒絕訪問。

避免將未使用的聲明放入 JWT。雖然 JWT 的大小沒有限制,但通常它們越大,簽名和驗證它們所需的 CPU 就越多,傳輸它們所需的時間也就越多。Benchmark 期望 JWT 瞭解性能特徵。

聲明和安全

擁有令牌的任何人都可以看到已簽名 JWT 的聲明。

正如您在上面看到的,要以明文形式查看聲明,您只需要一個 base64 解碼器,它可以在每個命令行和互聯網上的任何地方使用。

因此,您不應將任何應該保密的內容放入 JWT 中。這包括:

另一個安全問題與 aud 聲明的驗證有關。由於消費代碼已經擁有令牌,驗證 aud 聲明是否多此一舉?aud 聲明表明誰應該接收這個 JWT,但代碼已經有了它。不,總是驗證聲明。

爲什麼?

想象一下您有兩個不同 API 的場景。一個是創建和管理待辦事項,另一個是計費 API,用於轉賬。這兩個 API 都期望一些用戶具有 admin 角色。然而,就可以採取的行動而言,該角色意味着截然不同的事情。

如果待辦事項 API 和計費 API 均未驗證是否爲它們創建了任何給定的 JWT,則攻擊者可以從具有該 admin 角色的待辦事項 API 中獲取 JWT,並將其呈現給計費 API。

這最多是一個錯誤,最壞的情況是特權升級,對銀行賬戶產生負面影響。

JWT 簽名

JWT 的簽名很關鍵,因爲它保證了負載和標頭的完整性。驗證簽名必須是 JWT 的任何消費者執行的第一步。如果簽名不匹配,則不應進行進一步處理。

雖然您可以閱讀規範的相關部分 [4] 以瞭解簽名是如何生成的,但高級概述是:

當收到 JWT 時,可以執行相同的操作。如果生成的簽名正確,則 JWT 的內容與創建時沒有變化。

JSON Web 令牌限制

在規範中,JSON Web Tokens 的長度沒有硬性限制。實際上,考慮一下:

存儲

JWT 可以在 HTTP 標頭中發送,存儲在 cookie 中,並放置在表單參數中。在這些場景中,存儲決定了 JWT 的最大長度。

例如,瀏覽器中 cookie 的典型存儲限制通常爲 4096 字節,包括名稱。HTTP 標頭的限制因軟件組件而異,但 8192 字節似乎是一個常見值。

請查閱相關規範或其他資源以瞭解特定用例的限制,但請放心,JWT 沒有固有的大小限制。

性能影響

由於 JWT 可以包含許多不同類型的用戶信息,因此開發人員可能會忍不住在其中放入太多信息。這會降低簽名和驗證步驟以及傳輸中的性能。

對於前者的示例,以下是簽署和驗證兩個不同 JWT 的基準測試結果。每個操作進行了 50,000 次。

第一個 JWT 的正文長度約爲 180 個字符;總編碼令牌長度在 300 到 600 之間,具體取決於所使用的簽名算法。

hmac sign
  1.632396   0.011794   1.644190 (  1.656177)
hmac verify
  2.452983   0.015723   2.468706 (  2.487930)
rsa sign
 28.409793   0.117695  28.527488 ( 28.697615)
rsa verify
  3.086154   0.011869   3.098023 (  3.109780)
ecc sign
  4.248960   0.017153   4.266113 (  4.285231)
ecc verify
  7.057758   0.027116   7.084874 (  7.113594)

下一個 JWT 負載大約有 1800 個字符,因此是前一個令牌大小的十倍。這具有 2400 到 2700 個字符的總令牌長度。

hmac sign
  3.356960   0.018175   3.375135 (  3.389963)
hmac verify
  4.283810   0.018320   4.302130 (  4.321095)
rsa sign
 32.703723   0.172346  32.876069 ( 33.072665)
rsa verify
  5.300321   0.027455   5.327776 (  5.358079)
ecc sign
  6.557596   0.032239   6.589835 (  6.624320)
ecc verify
  9.184033   0.035617   9.219650 (  9.259225)

您可以看到,對於較長的 JWT,總時間增加了,但通常不是線性的。所用時間的增加範圍從 RSA 簽名的大約 20% 到 HMAC 簽名的大約 100%。

請注意傳輸更長的 JWT 所花費的額外時間;這可以用與任何其他 API 或 HTML 內容相同的方式進行測試和優化。

結論

已簽名的 JWT 具有標頭、正文和簽名。每個都在確保 JWT 可用於安全地存儲和傳輸關鍵信息(無論是否與身份有關)方面發揮着至關重要的身份驗證作用。瞭解所有這三個組件對於正確使用 JWT 也至關重要。

引用鏈接

[1] Fusion Auth Developer: https://fusionauth.io/learn/expert-advice/tokens/jwt-components-explained
[2] 在此處瞭解更多信息: https://datatracker.ietf.org/doc/html/rfc7515#appendix-C
[3] 此類聲明的示例: https://www.iana.org/assignments/jwt/jwt.xhtml
[4] 規範的相關部分: https://datatracker.ietf.org/doc/html/rfc7515#page-15

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/AGAD8u5IzIqgC2fxFiz3Ww