我在排查 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 信任鏈。