fixmint
ZH-TW

JWT 內容是什麼?為什麼解碼不等於驗證

作者: · · 閱讀約 4 分鐘

JSON Web Token(JWT)是由點號連接的三段文字:標頭、酬載與簽章。前兩段只是編碼,並未加密,因此持有權杖的人都能讀取內容。簽章則讓伺服器能確認權杖確實由受信任的一方發出,且沒有被竄改。解碼權杖可以告訴你它寫了什麼,只有驗證它,才能判斷是否該相信。

JWT 解碼器權杖過期了,還是仍然有效?免費使用

三個部分

權杖的格式是 header.payload.signature。以 jwt.io 上知名的範例權杖來說,第一段是:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

前兩段各自是把 JSON 轉成 base64url 的結果,這是一種可以安全用於網址的文字格式。第一段解碼後是:

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

同一個範例的酬載解碼後是:

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

酬載存放的是聲明(claims),也就是關於使用者或工作階段的陳述。簽章是由前兩段與一把金鑰計算出來的,因此只要改動一個字元,簽章就不再吻合。Base64url 不需要金鑰就能還原,所以絕對不要把密碼、私密金鑰或個人資料放進酬載。

已註冊的聲明

JWT 的標準 RFC 7519 保留了幾個簡短的聲明名稱:

  • iss(發行者):建立權杖的一方。
  • sub(主體):權杖描述的對象,通常是使用者 ID。
  • aud(對象):權杖預定使用的服務。
  • exp(到期時間):權杖失效的時間點。
  • nbf(生效時間):權杖最早可以使用的時間點。
  • iat(發出時間):權杖建立的時間。

時間是以 1970 年 1 月 1 日 UTC 起算的秒數,稱為 Unix 時間。範例中沒有 exp,依標準來說它永不過期。真實的權杖應該要有到期時間。其他名稱,例如 role 或 email,則由應用程式自行定義。

解碼不等於驗證

解碼是以點號切開權杖、還原 base64url,再讀取 JSON。它顯示的是權杖聲稱了什麼;偽造的權杖也會得到同樣的結果,因為任何人都能以任意簽章寫入任意聲明。

驗證則是用正確的金鑰重新計算簽章,並確認兩者吻合。驗證時也會檢查 exp 與 nbf 是否包住目前時間,以及 iss 與 aud 是否為服務預期的值。如果應用程式在沒有驗證的情況下就讀取 role 聲明,等於信任了任何寫入權杖的人。這是常見的錯誤。

HS256、RS256 與 ES256

alg 的值指出使用的演算法。常見的有三種:

  • HS256 是以 SHA-256 為雜湊的 HMAC。簽署與驗證使用同一把共享密鑰,因此這把密鑰必須只留在發行權杖的伺服器上。
  • RS256 是以 SHA-256 為雜湊的 RSA 簽章。發行者使用私密金鑰簽署,任何持有公開金鑰的人都能驗證。其他服務可以檢查權杖,卻無法製造權杖。
  • ES256 是使用 P-256 曲線與 SHA-256 的 ECDSA 簽章。運作方式與 RS256 相似,但金鑰與簽章較小。

當同一個服務既發行又檢查自己的權杖時,使用 HS256。當其他服務必須檢查不是自己發行的權杖時,使用 RS256 或 ES256。

信任標頭帶來的問題

標頭是由送出權杖的一方寫入的,因此驗證方不能讓標頭決定規則。

  1. alg: none。 沒有簽章的權杖可以宣告 "alg":"none"。有些函式庫曾經接受這類權杖,並略過檢查。應在自己的程式碼中設定允許的演算法,絕不要從權杖中讀取。
  2. 演算法混淆。 伺服器預期使用 RS256,並持有公開金鑰。攻擊者便送出一個 HS256 權杖,並以該公開金鑰作為 HMAC 的密鑰來簽署。如果驗證方接受標頭所指定的任何演算法,就會接受這個權杖。應將每把金鑰綁定到唯一一種演算法。
  3. 弱密鑰。 攻擊者擁有標頭、酬載與簽章,因此可以離線測試候選密鑰。短的密鑰或常見的單字很快就會被字典攻擊破解。RFC 7518 規定 HS256 的密鑰至少應為 256 位元,且必須是隨機產生的。

把正式環境的權杖貼到網站上

有效且未過期的權杖對持有者而言就等同於通行證,直到它過期為止。請像對待密碼一樣對待它。在貼上權杖之前,先確認該網站是否會把它傳送到伺服器。完全在瀏覽器分頁中執行的工具不需要這麼做。你可以用瀏覽器的開發人員工具觀察網路要求,來檢查任何網頁。

除非伺服器保留撤銷清單,否則權杖通常無法在到期前撤銷。如果有效的權杖外洩,請要求服務撤銷該工作階段或輪替金鑰。能用測試環境的權杖除錯時,就盡量使用測試環境的權杖。JWT 解碼器會在你的瀏覽器分頁中讀取三個部分,你貼上的內容不會傳送到任何地方。

Webhook:同樣的概念,搭配共享密鑰

大多數 Webhook 服務商會在標頭中送出請求內容的 HMAC,計算時使用你與他們共享的密鑰。你的接收端以相同方式計算 HMAC,並比對兩者。常見的失敗原因都很細微:雜湊前 JSON 被重新排版,位元組因此改變;測試用的密鑰被拿來對正式流量驗證;或是簽章以一般的字串相等比較,而這種比較可能洩漏時間資訊。請使用固定時間的比較方式。

Webhook 簽章測試工具會根據你貼上的密鑰與內容計算簽章,支援 GitHub、Stripe、Slack、Shopify 或任何 HMAC 方案,讓你看出兩個值是否吻合。

快速解答

JWT 有加密嗎?

沒有。標準的 JWT 是經過簽署的,任何人都能讀取它的標頭與酬載。只有簽章能保護它不被竄改。

如果我修改酬載會怎樣?

簽章就不再吻合,驗證的伺服器會拒絕這個權杖。讀取聲明並不會檢查簽章,所以解碼器仍會顯示你修改後的版本。

借助 AI 協助,由英文原文翻譯而成。

留言

    還沒有留言,搶先留言吧。

    您的評分(選填)

    您的電子郵件不會顯示,也不會被保存。

    Agentic AI in the Browser 的封面Agentic AI in the Browser

    隱私優先、在瀏覽器內執行的 AI 工作流程應用案例,也就是 fixmint 所依循的「在自己裝置上完成」理念。

    $4.99在 Gumroad 購買英文版