JWT 指南

JWT signature 驗證失敗時,我如何找出真正原因

遇到 invalid signature,我會從原始 compact token、algorithm allowlist、issuer、kid、JWKS、JWK compatibility、key rotation 與 cache 逐層排查。

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

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

JWT 簽章驗證失敗的除錯流程
先確認演算法、issuer、kid 與金鑰選擇,再調整驗證設定。

遇到 invalid signature 時,我第一件事不是換一把 key 碰運氣,而是確認 verification 使用的仍是原始 compact JWT bytes。JWS signature 是對編碼後的 header 與 payload 計算;把 JSON decode、pretty-print 再重新 encode,即使語意相同,signature input 也可能不同。

我的排查順序固定是:

raw token
→ allowed algorithm
→ expected issuer
→ trusted key source
→ kid candidate
→ key compatibility
→ actual signature verification
→ rotation/cache

1. 保存原始 token,不重新組裝

JWS signing input 概念上是:

BASE64URL(header) + "." + BASE64URL(payload)

因此我會以 Network 或原始 request 裡的 compact token 驗證,不會拿 decode 後 JSON 重新產生一顆「看起來一樣」的 JWT。

production 環境則避免把完整 bearer token 長期寫進 log。

2. 先確認 alg 是否在 allowlist

我會在 JWT Decoder 看:

{
  "alg": "RS256",
  "kid": "2026-key-b"
}

但 verifier 的 policy 應該先寫死:

allowed algorithms = ["RS256"]

如果 token 自己宣告 HS256、none 或其他不允許值,我希望它在 key lookup 前就失敗。

3. 確認是不是拿錯 issuer 的 key

不同 tenant、staging/prod 或不同 identity provider 可能出現相似 kid。我會:

  1. 讀 token iss;
  2. 與 expected issuer exact match;
  3. 只使用該 issuer 綁定的 trusted JWKS。

拿另一個 issuer 的 public key 做 verification,本來就應該失敗。

4. kid match 只是 key selection

找到相同 kid 後,我還檢查:

  • candidate 是否唯一;
  • kty 是否相容;
  • JWK alg 若存在是否一致;
  • use 是否為 sig;
  • key_ops 是否允許 verify;
  • EC curve 是否正確。

JWKS Validator 會把 candidate selection 與 signature result 分開。

5. 同一個 kid 不代表同一把 key

若環境或 rotation 過程重複使用 kid,cache 裡可能還是舊 key。這時可以用 JWK Inspector 的 RFC 7638 thumbprint 比較 public key material。

thumbprint 用來比較 identity 很有用,但不會取代 issuer trust。

6. HS256 我會看 secret bytes

常見問題:

  • production/staging secret 用錯;
  • env value 多了 newline;
  • 一邊把字串當 raw bytes,一邊先 Base64 decode;
  • rotation 後 verifier 還拿舊 secret。

測試時我使用 synthetic secret,不會把 production signing secret 隨意貼到外部服務。

7. RSA/EC key format 也可能出錯

直接用 PEM 時,要分清 public key、certificate、PKCS 格式與 library 要求。ECDSA 還有 JWS signature representation 與某些 crypto API DER 格式差異。

我不自己猜格式轉換,會依 JOSE library / Web Crypto 的明確規格處理。

8. Rotation 後新 token 才失敗,就查 JWKS cache

典型情況:

old kid → pass
new kid → unknown kid / invalid signature

我會確認:

  • new public JWK 是否在 signing switch 前 publish;
  • CDN / verifier cache 是否 stale;
  • unknown kid 是否觸發受控 refresh;
  • old/new key overlap 是否足夠。

詳見 JWT Key Rotation。

9. Signature 成功仍不代表整顆 JWT 可以接受

後面仍需檢查:

  • iss;
  • aud;
  • exp;
  • nbf;
  • token type;
  • scope/role。

也就是:

signature valid ≠ application accepted

我會保留的 regression tests

correct key + token → pass
wrong key → fail
modified payload → fail
unexpected alg → fail
unknown kid → fail
same kid but wrong key material → fail
stale JWKS → refresh 行為明確

我不採用的「修復」

  • 關閉 signature verification;
  • 允許所有 algorithm;
  • unknown kid 就拿任意 key 試;
  • 自動信任 token 的 jku;
  • issuer 不驗,只在 global key pool 裡找能通過的 key;
  • invalid signature 只記 warning 然後繼續。

我的 invalid signature 清單

  1. compact token 是否原封不動?
  2. alg 是否在 server allowlist?
  3. iss 是否為 expected issuer?
  4. key source 是否綁定 trusted issuer?
  5. kid candidate 是否唯一?
  6. kty/alg/use/key_ops/curve 是否相容?
  7. secret/public key bytes 是否真的正確?
  8. rotation/cache 是否 stale?
  9. crypto library 的 key/signature format 是否正確?
  10. signature 後是否仍做 claims validation?

我解決 invalid signature 的目標,不是讓錯誤訊息消失,而是重新建立一條能說明的信任鏈:哪個 issuer、哪個 trusted key source、哪一把 JWK、哪個 algorithm,最後真的驗證了這份原始 signing input。

來源與參考資料

相關指南