JWT の署名検証に失敗したとき、私は token を編集したり、検証設定を片っ端から変えたりしません。元の compact JWT を保存し、header の alg / kid、payload の iss を読んで、アプリが信頼する鍵の取得元から逆向きに確認します。
期待する HMAC secret / RSA public key があるなら JWT 署名検証 で元の header.payload bytes をそのまま検証します。
署名検証エラーが示すこと
signed JWT/JWS は:
header.payload.signature
で、signature は encoded header + payload に対して作られます。
失敗の主な候補は:
- HMAC secret が違う
- RSA public key が違う
- issuer / tenant が違う
- key rotation 後も古い鍵を使っている
kidで別の鍵を選んでいる- 想定外の algorithm
- token bytes が変更されている
- key / PEM の形式や encoding が合っていない
です。
Step 1: 元の token を変更しない
payload の role を user から admin に変えるだけでも signed bytes は変わります。
JSON の意味が同じに見えても signature は意味上の object ではなく bytes に対して計算されます。私はデバッグ前に元の compact JWT を保ち、segment を再構成しません。
Step 2: alg / kid / iss を読む
{
"alg": "RS256",
"kid": "key-42"
}
これらは手掛かりであって、信頼そのものではありません。
alg は token が使用したと主張する algorithm、kid は signer が選択してほしい鍵 ID、iss は発行者だと主張する値です。
実際に許可する algorithm / issuer / key は application config が決めます。
Step 3: 期待する algorithm を固定する
application が RS256 を期待するなら verifier は RS256 の allowlist を持つべきです。header が HS256 と主張したからといって自動で切り替えません。
RFC 8725 が algorithm を明示的に制限するよう求める理由でもあります。
Step 4: key を issuer まで追う
HS256 なら shared secret の正確な bytes を確認します。
RS256 なら public key が信頼する issuer に属することを確認します。
よくある原因:
- production token を development key で検証している
- tenant が違う
- PEM の header/footer が欠けている
- certificate と SPKI public key の形式が合っていない
- JWKS cache が古い
- 別の JWK を選んでいる
任意の key で signature が通っても意味はありません。application が信頼すると決めた取得元の key で通る必要があります。
Step 5: kid と rotation を見る
kid があるなら、信頼する key set と正確に一致するか確認します。
key が見つからない場合は:
- JWKS cache が古い
- issuer が key rotation した
- environment が違う
- issuer URL が違う
- key selection の実装に問題がある
といった原因を疑います。
kid は key selection のための hint であり、identity proof ではありません。詳しくは JWT kid ガイド を使います。
Step 6: HMAC secret の encoding
HS256 では同じ設定文字列に見えても、実際の bytes が違うことがあります。
- raw UTF-8 か Base64 decode 後の bytes か
- 余計な whitespace / newline が入っていないか
- environment ごとに secret が違わないか
- rotation 前後の secret が混在していないか
を確認します。
弱い password に変えて通すのは修正ではありません。RFC 8725 は弱い HMAC secret に対する offline guessing risk を指摘しています。
Step 7: signature が通っても validation は続く
signature 成功後も:
exp/nbfissaud- token type
- scopes / roles
を確認します。
cryptographic layer が正しいだけで API acceptance は完了しません。JWT Validator と JWT デバッガー を続けて使います。
私がやらない shortcuts
- signature verification を無効化する
- token が指定した algorithm をすべて受け入れる
- token が指定した任意の URL から key を取得して信頼する
- payload を編集して古い signature が通ると考える
- decode success を verify success と扱う
私の invalid-signature checklist
- 元の compact JWT を保存する。
- decode して
alg/kid/iss/audを見る。 - application の allowed algorithm を確認する。
- trusted issuer と expected key source を確認する。
kidを現在の trusted key set と比較する。- rotation / environment / tenant mismatch を確認する。
- HMAC なら secret bytes / encoding を見る。
- RSA なら public key format と provenance を見る。
- JWT 署名検証 を trusted key で実行する。
- signature が通るのに 401 なら issuer / audience / time へ進む。
invalid signature は単なる parse error ではありません。verifier が想定した信頼経路で integrity / authenticity を確立できていないという signal なので、私は bypass せず trust path を修正します。