# Json Web Token ![JWT](https://kylinyu.win/img/post/jwt.jpg) Json Web Token 簡稱 JWT,是一個開放的工業標準,用於資料傳輸時的安全驗證。透過將重要訊息加上私有的金鑰進行加密可以大幅的提升資料的安全性。 <!-- more --> **傳統Session的認證** 用戶向應用程式提供了帳號密碼進行認證,下一次 Request 的時候用戶還要再進行一次認證 根據 Http 協議,Http 本身是一種無狀態的協議,應用程式無法知道是哪個用戶發出 Request 應用程式會在 Server 儲存一份用戶的登入資訊,並將這份登入資訊傳給瀏覽器,讓瀏覽器保存為 Cookie 以便下一次發送Request時一起傳給應用程式,讓應用程式可以識別出是哪個用戶 基於 Session 驗證會有以下幾個問題 : * 若每次 Request 都存到 Server 暫存檔會造成 Server 負擔太大 * 大部分大流量的網站會部署在多台主機上,若是只暫存在某台主機上,下次用戶被導向到其他台主機會找不到之前的 Session 暫存檔 * 若是 Cookie 在途中被攔截,用戶很容易遭受到偽造的 Request 攻擊 關於 Session 和 Cookie 的更多介紹請參考 [Session & Cookie](https://tienyulin.github.io/session-cookie/)。 **Token-based Authentication** 類似於 Cookie-based 的流程,但是 Server 會在 Response 時回傳一組加密的 Token(JWT 格式),以提高安全性 流程 : 1. 瀏覽器發送帳號密碼到 Server 2. Server 驗證帳號密碼成功後便產生一組加密 Token 放在資料庫 3. Server 將 Token 回傳給瀏覽器(可儲存在 Cookie 或是用戶端的儲存空間) 4. 下次進入網站時,Request 中 Cookie 的 Token 會一併傳給 Server,Server 會將 Token 解開做驗證,若驗證成功則繼續處理 Request ## 使用 JWT 的優缺點 **優點** : * 採用 JSON object 格式,大部分程式語言皆支援 * 可存放非敏感的使用者資訊 * Payload 不多則 JWT 的 size 相當小 * 不需在資料庫存放 Session,適合多台 Server 情境 * 針對手機 APP 的應用非常方便,不用每次打開 APP 都要重新登入 * 支援跨域請求([CORS](https://developer.mozilla.org/zh-TW/docs/Web/HTTP/CORS)),不會有傳統用 Cookie 進行請求的問題 **缺點** : * JWT 無法主動被中止,也就是不能像 Session 一樣被強制無效,但仍有方法可以避免 * JWT 一旦洩漏會有很大的安全性問題,通常透過以下兩種方式 1. 用戶電腦遭到駭客竊料 2. 駭客中途攔截擷取封包,並獲取 JWT,但使用 Https 可以大幅降低此種攻擊,只要定期更換SSL即可 ## JWT的組成 JWT 是由三段訊息組成 Header, Playload, Signature 將三段資訊用`.`連接起來即可組成 JWT 格式的字串,如下方字串 ``` eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwaG9uZSI6IjA5MTIzNDU2NzgiLCJuYW1lIjoiVG9ueSBMaW4iLCJzZXgiOiJNYWxlIiwiYWRtaW4iOnRydWUsImlhdCI6IjIwMjAvMDUvMTQifQ.4Lyuh-1wAvYDnqHfqApHaQH7YZRJpFI8byhlnGreza0 ``` ### 1. Header Header 包含了兩個訊息 * 類型(typ) : 這裡使用 jwt * 加密演算法(alg) : 通常直接使用 HMAC SHA256 進行 base64 編碼 範例 ```json= { "alg": "HS256", "typ": "JWT" } ``` 以 base64 加密後(可以對稱解密),此即為第一段 ``` eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ``` ### 2. Payload(載荷) 放聲明(Claim)的地方,也就是要傳遞的資料,定義上有3種聲明 : 1. Registered(註冊) 公認建議可以放,但不強迫 * iss(Issuer) : JWT 的核發者 * exp(Expiration Time) : JWT 的過期時間,需大於核發時間 * sub(Subject) : JWT 所面向的用戶 * aud(Audience) : 接收 JWT 的一方 * nbf(Not Before) : 定義發放 JWT 之後,在某個時間點以前此 JWT 仍是不能使用的 * iat(Issued At) : JWT 的核發時間 * jti(JWT Id) : JWT 的識別碼,不得重複 2. Public(公開) 定義官方的公開聲明,不建議存放敏感訊息,因為可以被解開,實務上不太會用到 3. Private(私有) 發放 JWT 的 Server 可以自己定義,通常也不會放敏感訊息(e.g, 密碼),實務上會放使用者帳號、用戶名稱等 範例 ```json= { "phone": "0912345678", "name": "Tony Lin", "sex": "Male", "admin": true, "iat" : "2020/05/14" } ``` 以 base64 加密後(可以對稱解密),此即為第二段 ``` eyJwaG9uZSI6IjA5MTIzNDU2NzgiLCJuYW1lIjoiVG9ueSBMaW4iLCJzZXgiOiJNYWxlIiwiYWRtaW4iOnRydWUsImlhdCI6IjIwMjAvMDUvMTQifQ ``` ### 3. Signature 由三部分組成,base64UrlEncode(header)、base64UrlEncode(payload)、secret ``` HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) ``` Header 和 Playload 以`.`來串接,secret 則是放在 Server 端的秘密字串,將這三個部分接在一起後進行加密 secret 需小心保存不得外洩,一旦外洩則用戶端就可以自己產生 JWT,並透過 JWT 存取資源 範例 : ``` HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), 123456 ) ``` 此即為第三段 ``` 4Lyuh-1wAvYDnqHfqApHaQH7YZRJpFI8byhlnGreza0 ``` ### 合併三段字串 最後由`.`來合併三段字串組成 JWT 字串,此字串即為一個合法可以核發的 JWT 字串 > Header.Payload.Signature ``` eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwaG9uZSI6IjA5MTIzNDU2NzgiLCJuYW1lIjoiVG9ueSBMaW4iLCJzZXgiOiJNYWxlIiwiYWRtaW4iOnRydWUsImlhdCI6IjIwMjAvMDUvMTQifQ.4Lyuh-1wAvYDnqHfqApHaQH7YZRJpFI8byhlnGreza0 ``` ## 使用方法 在發 Request 時,在 Header 加入 Authorization: Bearer &lt;token&gt; 範例 : ```csharp= post('api/user/1', { headers: { 'Authorization': 'Bearer ' + token } }) ``` Server 收到後會去檢查 JWT token 是否有效 ![JWT Process](https://cdn.auth0.com/content/jwt/jwt-diagram.png) ## 參考 [1] [jwt.io](https://jwt.io/) [2] [JWT(JSON Web Token) — 原理介紹](https://medium.com/%E4%BC%81%E9%B5%9D%E4%B9%9F%E6%87%82%E7%A8%8B%E5%BC%8F%E8%A8%AD%E8%A8%88/jwt-json-web-token-%E5%8E%9F%E7%90%86%E4%BB%8B%E7%B4%B9-74abfafad7ba) ###### tags: `授權` `Json Web Token` `JWT`