2022年1月11日

JWT 是什麼?跟 Cookie 差別在哪裡?兩個常用使用者驗證機制的比較

JWT 概念介紹

JWT 是一種 Token,它的全名是 JSON Web Token。它由伺服器端產生後,交付給客戶端使用,Token 中會夾帶取多資訊,包含用戶的驗證資料或其他。

JWT 的表現形式是一個純粹的字串,這個字串有三個部分,分別為 Header、Payload、Signature,這三個部分會串接起來,用 . 來分隔,形成這樣的格式:Header}.{Payload}.{Signature},實際範例如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.B3GHLnjMFsZJc3K97UIWN68E8WovKxO0Qp6Ye4sVLzo

JWT 格式與驗證方式

JWT 透過驗證簽章 (Signature) 的方式,確保客戶端送過來的資訊是正確無誤的。

在談論簽章之前,我們先回來看一下 JWT 的格式,前面有提到 JWT 的 J 代表的是 JSON,可是這個東西看起來一點都不像我們熟悉的 JSON 格式?

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.B3GHLnjMFsZJc3K97UIWN68E8WovKxO0Qp6Ye4sVLzo

其實每個部分都有另外經過 Base64Url 編碼。

Header

經過 Base64Url 編碼前的 Header 其實是這個樣子:

{
  "alg": "HS256",
  "typ": "JWT"
}

這個形式就是我們熟悉的 JSON 了。alg 的部分表示簽章時所使用的演算法,這邊是 HS256,typ 則是指 Token 的類型,也就是 JWT 。

把上述格式的 JSON 去掉空白及換行符號,會得到:

{"alg":"HS256","typ":"JWT"}

將這串 JSON 進行 Base64Url 編碼,就會得到範例 Token 中的第一段。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Payload

跟前面一樣,Base64Url 編碼前的 Payload 是大家熟悉的 JSON 格式:

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

在 payload 這邊可以用來放置使用者的資料,JWT 的術語稱為 claims,不確定怎麼翻譯比較準確,可以翻譯成宣稱或主張。應該是用於告訴使用不能只看這些 claims 就相信這些資料是正確的,必須透過簽章來檢驗正確性。

這邊的 sub 是 Subject 的縮寫,可以想像成使用者 id, iat 則是 Issued At 的縮寫,subiat 都算是 JWT 慣例的 claims,我們也可以放自己的 claims, 例如 name 就是使用者自己設定的 (詳情請看 JWT 官網及 RFC 文件)。

一樣,我們將 Palyload 去除掉空白及換行符號後得到:

{"sub":"1234567890","name":"John Doe","iat":1516239022}

將這串 JSON 進行 Base64Url 編碼,就會得到範例 TOKEN 中的第二段。

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ

Signature

簽章就是 JWT 的核心所在。在前面的 Header 區塊中,有提到使用了使用了 HS256 作為簽章演算法,HS256HMAC SHA256 的縮寫。

根據官方網站的說明,Signature 的規則是:

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

也就是將 Base64Url 編碼後的 header 及 payload 串在一起後,再搭配 secret 來產生簽章。

這邊我使用的範例 secret 是 JWTDevinDeving,所以實際上是這樣計算:

HMACSHA256(
  eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 + "." +
  eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ,
  JWTDevinDeving
)

最後得到我們的 Signature,也就是範例 Token 的第三段:

B3GHLnjMFsZJc3K97UIWN68E8WovKxO0Qp6Ye4sVLzo

JWT 驗證原理

雜湊函式簡介

HMAC SHA256 是一種雜湊函式,簡單來說,就是一種會把字串轉換成亂碼的演算法。但特別的地方在於固定的字串會產生固定的亂碼,因此只要字串一改變,就會產生不一樣的亂碼。

我們用簡單的雜湊算法 md5 為例,

md5(apple) = 1f3870be274f6c49b3e31a0c6728957f
md5(appla) = df4c9efc0c4baefb6488cbef9678f11b
md5(banana) = 72b302bf297a228a75730123efef7c41

( md5 線上轉換工具 )

我們可以把 HMAC SHA256 想像成比較複雜精密的 md5,只要 header 跟 payload 有變化,就會產生出不一樣的字串。

但 HMAC 除了轉換的方式比較精密以外,它還需要配合 secret 使用。有了 secret,即便是一樣的原文,只要 secret 不一樣就會產生不一樣的結果。

例如,我將 JWTDevinDeving 換成 FalseJWTDevinDeving 後,則產生的 Signature 就會從原本的:

B3GHLnjMFsZJc3K97UIWN68E8WovKxO0Qp6Ye4sVLzo

變成:

Hr6Q7tsrzEu7OynS76nYJRUPphRfTKuBJDM748MGOso

JWT 如何利用雜湊函式來驗證

伺服器端在產生 Token 時,會使用自己的 secret 對 header 及 payload 進行簽章,產生 signature 後,製作成 JWT 後給客戶端使用。

客戶端下次提供 JWT 給伺服器端時,伺服器端會檢查客戶端所提供的 JWT 是否有效。

檢查的方式是,再產生一次 signature,並比對是否一致。

伺服器端會將客戶端提供的 JWT 的 header 及 payload 取出,再用 secret 來製作一次 signature,如果新製造出來的 signature 跟客戶端提供的 JWT 中的 signature 一致,伺服器端就會相信這個 JWT 是有效的。

這樣的流程是利用前面提到的 HMAC 的兩個特性,

  1. 只要內容有變動,signature 就一定會改變。
  2. 只要secret 不一樣,signature 就會不一樣。

所以只要 secret 不外流,惡意使用者即便串改內容,也沒辦法產生有效的 signature。

Cookie

Cookie 是什麼

Cookie 由伺服器端透過回應 (HTTP Response) 傳送給客戶端,並由客戶端儲存在瀏覽器中。Cookie 以一筆資料對應一個鍵值的方式儲存,也就是所謂的 key-value 格式。並且會包含一些額外資訊,例如失效時間、Domain 等。

在每次的請求 (Http Request) 中 ,瀏覽器會自動將相同 Domain 的 Cookie 夾帶在請求中送出。一個 Domain 可以有很多筆 Cookie,所以在請求中,所有相同 Domain 的 cookies 都會被瀏覽器一起帶過去給伺服器。

Cookie 可以被用來記錄使用者資訊,並透過這樣的方式來達到身分驗證、個人化、使用者行為追蹤等目的。

Cookie 如何驗證使用者身分

cookie 是客戶端的技術,結合伺服器端的技術 session,可以達到身分驗證的目的。

  1. 使用者透過瀏覽器送出了一筆請求給 https://test.com/hello
  1. 伺服器收到了訊息,並回應給瀏覽器,同時夾帶了 cookie id=123
  2. 瀏覽器收到的回應,並將 cookie id=123 儲存在瀏覽器中。

之後,

  1. 使用者送出了另一筆請求給 https://test.com/nice-to-meet-you。這時因為瀏覽器中有儲存 https://test.com 的 cookie id=123,因此瀏覽器會自動將該 cookie 帶過去。
  2. 伺服器收到訊息,發現請求中有包含 cookie id=123,瀏覽器就可以知道這次的請求跟之前是同一個人。

在上面的例子中,其實伺服器在第一次傳送 cookie id=123 給客戶端時,也同時透過 session 儲存了 id=123 的資訊。

所以在之後從客戶端收到 id=123 的 cookie 時,伺服器就可以把 123 拿去與 session 中的資料做比對,確認是否存在。

這邊先簡單帶過 cookie 與 session,以後有機會在專文介紹。

JWT 跟 Cookie 比較

  1. JWT 是一種資料格式;Cookie 是一種儲存方式
  2. Cookie 的 value 可以儲存任何字串,包含 JWT。
  3. JWT 如果不是存在 Cookie,通常必須要另外處理,才會帶給伺服器。
  4. 針對身分驗證用途,伺服器通常不儲存產生的 JWT,但使用 Cookie 時,通常會在伺服器儲存一份 Session 資料。