JWT 內容是什麼?為什麼解碼不等於驗證
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。
信任標頭帶來的問題
標頭是由送出權杖的一方寫入的,因此驗證方不能讓標頭決定規則。
alg: none。 沒有簽章的權杖可以宣告"alg":"none"。有些函式庫曾經接受這類權杖,並略過檢查。應在自己的程式碼中設定允許的演算法,絕不要從權杖中讀取。- 演算法混淆。 伺服器預期使用 RS256,並持有公開金鑰。攻擊者便送出一個 HS256 權杖,並以該公開金鑰作為 HMAC 的密鑰來簽署。如果驗證方接受標頭所指定的任何演算法,就會接受這個權杖。應將每把金鑰綁定到唯一一種演算法。
- 弱密鑰。 攻擊者擁有標頭、酬載與簽章,因此可以離線測試候選密鑰。短的密鑰或常見的單字很快就會被字典攻擊破解。RFC 7518 規定 HS256 的密鑰至少應為 256 位元,且必須是隨機產生的。
把正式環境的權杖貼到網站上
有效且未過期的權杖對持有者而言就等同於通行證,直到它過期為止。請像對待密碼一樣對待它。在貼上權杖之前,先確認該網站是否會把它傳送到伺服器。完全在瀏覽器分頁中執行的工具不需要這麼做。你可以用瀏覽器的開發人員工具觀察網路要求,來檢查任何網頁。
除非伺服器保留撤銷清單,否則權杖通常無法在到期前撤銷。如果有效的權杖外洩,請要求服務撤銷該工作階段或輪替金鑰。能用測試環境的權杖除錯時,就盡量使用測試環境的權杖。JWT 解碼器會在你的瀏覽器分頁中讀取三個部分,你貼上的內容不會傳送到任何地方。
Webhook:同樣的概念,搭配共享密鑰
大多數 Webhook 服務商會在標頭中送出請求內容的 HMAC,計算時使用你與他們共享的密鑰。你的接收端以相同方式計算 HMAC,並比對兩者。常見的失敗原因都很細微:雜湊前 JSON 被重新排版,位元組因此改變;測試用的密鑰被拿來對正式流量驗證;或是簽章以一般的字串相等比較,而這種比較可能洩漏時間資訊。請使用固定時間的比較方式。
Webhook 簽章測試工具會根據你貼上的密鑰與內容計算簽章,支援 GitHub、Stripe、Slack、Shopify 或任何 HMAC 方案,讓你看出兩個值是否吻合。
快速解答
JWT 有加密嗎?
沒有。標準的 JWT 是經過簽署的,任何人都能讀取它的標頭與酬載。只有簽章能保護它不被竄改。
如果我修改酬載會怎樣?
簽章就不再吻合,驗證的伺服器會拒絕這個權杖。讀取聲明並不會檢查簽章,所以解碼器仍會顯示你修改後的版本。
借助 AI 協助,由英文原文翻譯而成。
留言
還沒有留言,搶先留言吧。


