JWT 指南

我如何分清 JWK、JWKS、kid 與真正的 JWT 驗證

JWK 是單一 JSON Web Key,JWKS 是多個 JWK 組成的 key set。我用 issuer、jwks_uri、kid、key compatibility 與 signature verification 串成完整信任流程。

作者:JWTDecoder.tools最後確認 2026-09-13

編輯語氣說明:第一人稱敘述代表 JWTDecoder.tools 已文件化的驗證工作流程;發布者與組織作者皆為 JWTDecoder.tools。

可信 issuer、JWKS、kid 與 JWK 關係圖
可信 issuer 發布 JWKS,kid 從中選出 JWK,再用該公開金鑰驗證簽章。

我在排查 JWT signature 時,會先把 JWK 與 JWKS 分開:JWK 是一把 key 的 JSON 表示;JWKS(JSON Web Key Set) 是一個包含多把 JWK 的文件。 這個差異一旦清楚,issuer → jwks_uri → kid → JWK → verify 就不容易混亂。

JWK 是什麼

RSA public JWK 可能長這樣:

{
  "kty": "RSA",
  "kid": "2026-key-b",
  "use": "sig",
  "alg": "RS256",
  "n": "...",
  "e": "AQAB"
}

我常看:

  • kty:key type;
  • kid:key identifier;
  • alg:標示的 algorithm;
  • use:常見 sig / enc;
  • key_ops:例如 verify;
  • RSA 的 n/e;
  • EC 的 crv/x/y。

這些 metadata 描述 key,但不會自動證明它值得信任。

JWKS 是什麼

JWKS 的核心是 keys array:

{
  "keys": [
    { "kid": "old-key", "kty": "RSA", "...": "..." },
    { "kid": "new-key", "kty": "RSA", "...": "..." }
  ]
}

rotation 期間 old/new public key 同時存在是很常見的情況。

我驗證 JWT 的完整順序

configured trusted issuer
→ trusted metadata
→ trusted jwks_uri
→ JWKS keys[]
→ 用 JWT kid 選 candidate
→ 檢查 alg/kty/use/key_ops/curve
→ 驗證 signature
→ 驗證 iss/aud/exp/nbf

kid 的位置很重要:它是在已信任的 key set 裡選 key,不是用來建立 trust。

kid match 不等於 signature verified

header:

{ "alg": "RS256", "kid": "new-key" }

找到 kid=new-key 的 JWK 後,我還會確認:

  • candidate 是否唯一;
  • kty 是否與 algorithm family 相容;
  • JWK alg 若存在是否相容;
  • use 若存在是否為 sig;
  • key_ops 若存在是否包含 verify;
  • EC curve 是否相容。

最後還要真的做 signature verification。JWKS Validator 會把「找到 key」與「signature 通過」分開呈現。

為什麼 JWKS 常有多把 key

最常見原因就是 rotation。issuer 可能先公開 new public key,過一段時間才開始用 new private key 簽 token;同時 old token 還沒過期,所以 old public key 不能立刻刪除。

完整流程可看 JWT Key Rotation。

RFC 7638 JWK Thumbprint 的用途

JWK Inspector 可以計算 RFC 7638 SHA-256 thumbprint。我會用它比較兩個環境裡的 public key material 是否相同。

它適合做 key identity comparison,但thumbprint 不等於 trust。一把攻擊者提供的 key 也可以計算 thumbprint。

我不自動信任 token 裡的 jku

JWT header 可以帶 jku 或 x5u。如果 verifier 直接按照 token 給的 URL 抓 key,再用那把 key 驗 token,就可能讓 untrusted input 同時決定 key source。

我會反過來:

application 先知道 trusted issuer
→ issuer metadata 決定 JWKS 來源
→ token header 只參與 trusted set 內的 key selection

OIDC/OAuth metadata 如何連到 JWKS

metadata 可能是:

{
  "issuer": "https://id.example.com/",
  "jwks_uri": "https://id.example.com/.well-known/jwks.json"
}

我會用 OIDC Discovery Checker 檢查 returned issuer 是否與預期 issuer exact match,以及 jwks_uri 是否合理。

Unknown kid 怎麼處理

如果 cached JWKS 找不到新的 kid,我會允許一次受控、rate-limited 的 JWKS refresh,再查一次;仍找不到就 fail closed。

我不會讓每個 random kid 都觸發無限制 remote fetch,避免 refresh storm。

Duplicate kid 怎麼處理

同一個 JWKS 若有多個完全無法區分的相同 kid,我傾向把它視為 ambiguous error,而不是永遠選第一個 array element。

key selection 應該是 deterministic 且能解釋的。

我最後怎麼記住 JWK/JWKS

  • JWK:一把 key 的 JSON 表示;
  • JWKS:多把 JWK 的集合;
  • kid:trusted set 裡的 key selector;
  • thumbprint:比較 key identity 的 digest;
  • signature verification:真正的密碼學驗證;
  • issuer/audience validation:應用程式的 trust policy。

當 signature 出錯時,我不會停在「kid 有找到」。我會一路確認 issuer、JWKS 來源、candidate JWK、algorithm compatibility、實際 signature 與 claims policy,這才是一條完整的 JWT 信任鏈。

來源與參考資料

相關指南