[譯]5個簡單的步驟去理解 JSON Web Tokens(JWT)

命名最頭痛發表於2019-02-04

圖片描述

原文作者:Mikey Stecky-Efantis
原文地址:5 Easy Steps to Understanding JSON Web Tokens(JWT)
譯者:命名最頭疼

在本文中, 將解釋JSON Web Tokens(JWT)的基本原理以及使用他們的原因。JWT 是確保你應用程式信任和安全的重要部分。JWT 允許以安全的方式來宣告,例如使用者資料。

為了解釋JWT如何工作,讓我們從一個抽象的定義開始。

一個 JSON Web Token(JWT)是一個 JSON 物件,在 RFC7519 中定義為表示兩方之間的一組資訊的安全方式。該令牌由標頭,有效負載和簽名組成。 簡單來說,JWT 只是一個具有以下格式的字串

  header.payload.signature
複製程式碼

應該注意的是,雙引號字串實際上被視為有效的 JSON 物件。

下面將展示實際使用 JWT 的方式和原因,我們將使用一個簡單的例子(如下圖所示),這個例子中的實體是使用者,應用伺服器,和認證伺服器。認證伺服器將提供 JWT 給使用者,通過 JWT,使用者可以安全的和應用伺服器間進行通訊。

圖片描述
在這個例子中,使用者第一次進入認證伺服器並使用認證伺服器登陸系統(例:在 Facebook 和 Google 中通過使用者名稱和密碼登陸,等)。認證伺服器建立JWT並且發生給使用者,當使用者對應用程式進行 API 呼叫時,JWT將隨著API一併傳遞。在此配置中,應用程式伺服器將會進行認證配置,用於驗證傳入的JWT是否是由身份伺服器建立的(稍後將詳細解釋驗證過程)。因此,當使用者使用帶有JWT的API去發起呼叫請求時,該應用能夠使用JWT去認證這個API是否來自被認證的使用者。

現在,將更深入地研究JWT本身及其構建和認證的方式。

Step1. 建立令牌頭

JWT 的頭部包含有關如何計算 JWT 簽名的資訊,其標頭是以下格式的 JSON 物件

{
  "typ": "JWT",
  "alg": "HS256"
}
複製程式碼

在上述 JSON 中,"typ" 鍵值指定了 JWT 物件,"alg"鍵值指定使用哪種雜湊演算法來建立 JWT 簽名元件。在這個例子中,我們使用 HMAC-SHA256 演算法(一種使用金鑰的雜湊演算法)來計算簽名(在步驟 3 中更詳細地討論)。

Step2. 建立 PayLoad

PayLoad是儲存在 JWT 裡的內部資料(該資料也稱為 JWT 的 “宣告”)。在這個例子中,認證伺服器建立一個JWT 用於儲存使用者資訊,特別是使用者ID。

{
  "userId": "b08f86af-35da-48f2-8fab-cef3904660bd"
}
複製程式碼

在這個例子中,我們只將一個宣告放入 payload 中,你也可以根據需要新增任意數量的宣告。JWT關鍵資訊(payload)有幾種不同的標準宣告,例如 "iss" 表示 issuer,"sub" 表示 subject還有 "exp" 表示expiration time。建立 JWT 時,這些欄位非常有用,但是他們是可選的,想了解更多有關 JWT 標準欄位的詳細資訊,請參閱 JWT 上的維基百科頁面。

請記住,資料的大小會影響 JWT 的整體大小,通常這不是問題,但是,JWT 太大可能會對效能產生負面影響並導致延遲。

Step3. 建立簽名

簽名的計算方式通過以下的虛擬碼進行表述

  // signature algorithm
  data = base64urlEncode( header ) + “.” + base64urlEncode( payload )
  hashedData = hash( data, secret )
  signature = base64urlEncode( hashedData )
複製程式碼

該演算法的作用是通過 base64url 對步驟1和步驟2中建立的頭和關鍵資訊(payload)進行編碼。然後通過點(.)來連線兩個編碼字串,構成資料 data 。在 JWT 頭部使用指定的雜湊演算法對資料字串使用金鑰進行雜湊,並將生成的雜湊資料分配給 hashedData。然後對該雜湊資料進行 base64url 編碼以產生 JWT 簽名。

在該例子中,頭部和關鍵資訊(payload)都是 base64url 編碼的

// header
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
// payload
eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ
複製程式碼

然後,在編碼頭和編碼關鍵負載(payload)中週期的加入攜帶金鑰的應用指定簽名演算法,於是,我們得到簽名所需的雜湊資料。在該例子中,這意味著應用HS256演算法,並將金鑰設定為字串 "secret",在資料字串上獲取 hashedDate 字串,之後,通過 base64url 編碼 hashedData 字串,我們得到以下 JWT 簽名

// signature
-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM
複製程式碼

Step4. 將 JWT 的所有元件(3個)組合在一起

我們已經建立了所有的元件(3個),現在我們可以通過它們來建立 JWT了。請記住 JWT 的結構 header.payload.signature ,我們使用通過 base64url 編碼的 header 和 payload,以及步驟 3 中籤署的簽名,只需要組合這些元件並通過句號(.)分隔它們。

// JWT Token
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ.-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM
複製程式碼

通過瀏覽器,在jwt.io上你可以嘗試建立屬於自己的 JWT。

回到這個例子,現在該認證伺服器能夠傳送 JWT 給使用者了。

JWT 如何保護我們的資料?

要理解使用 JWT 的目的,並而不是通過任何的方式手段去隱藏或者模糊資料。使用 JWT 的原因是為了證明傳送的資料實際上是由真實的源建立的。

如上述步驟所示,JWT 內的資料經過編碼和簽名而不是加密的。編碼資料的目的是轉換資料的結構。一方面簽名資料允許資料接收器驗證資料來源的真實性。因此,編碼和簽名資料不會保護資料。另一方面,加密的主要目的是保護資料並防止未經授權的訪問。有關編碼和加密之間差異的詳細說明,以及有關雜湊如何工作的更多資訊,請參閱此文章

由於 JWT 僅被簽名和編碼,並且由於 JWT 未加密,因此 JWT 不保證敏感資料的任何安全性。

Step5. JWT 驗證

在第三個例子中,我們使用由 HS256 演算法簽名的 JWT,其中只有身份驗證伺服器和應用伺服器知道金鑰。當應用程式設定其身份認證的時候,應用伺服器從身份驗證伺服器接收金鑰。由於應用程式知道金鑰,因此當使用者對應用程式呼叫JWT 連線的 API 時,應用程式可以在 JWT 上執行與步驟 3 相同的簽名演算法。然後該應用程式能夠驗證自身通過雜湊操作獲得的簽名與 JWT 本身得到的簽名是否匹配(即,它與由認證伺服器建立的 JWT 簽名匹配)。如果簽名匹配,這意味著 JWT 有效,表示 API 的呼叫是來自認證伺服器的。除此之外,如果簽名不匹配,則表示收到的 JWT 無效,這意味著你的應用程式正受到潛在的攻擊。因此,通過驗證 JWT ,應用程式在自身和使用者之間新增了一層信任。

結論

我們瞭解了 JWT 是什麼,如何建立和驗證它們,以及如何使用它們來確保應用程式與其使用者之間的信任關係。這是瞭解 JWT 的基礎和起點。在確保應用程式中的信任和安全性難題中,JWT 只是其中之一。

應該注意的是,本文中描述的 JWT 認證設定使用的是對稱金鑰演算法(HS256),你也可以以類似的方式設定 JWT 身份驗證,除非你使用非對稱演算法(例如:RS256)這類演算法的身份驗證伺服器具有金鑰,並且應用程式伺服器具有公鑰。檢視此 Stack Overflow 問題瞭解對稱和非對稱演算法的差異性及其詳細分類。

還應該注意,JWT 應該通過 HTTPS(而不是 HTTP)連線。HTTPS 可以有效的防止未經授權的使用者通過攔截伺服器和使用者之間通訊的方式來傳送 JWT。

此外,如果你的 JWT 關鍵資訊(payload)有一部分過期了,那麼整個 JWT 將被視為無效,不能再使用了。

相關文章