DevToolBox

JWTの署名検証に失敗する原因と対処

最終更新日: 2026-04-19公開日: 2026-04-19執筆: DevToolBox編集部

JsonWebTokenError: invalid signaturesignature verification failed は JWT を扱うサーバで最も多いエラーの一つです。 原因は 5 つに大別でき、切り分け順さえ決めれば短時間で特定できます。

invalid signature and signature verification failed are the most common JWT errors on the server side. There are five typical causes; a fixed diagnostic order gets you to the root quickly.

JWT Decoder

署名検証に失敗したトークンを貼り付けて、ヘッダーのalgとペイロードを確認。HS256の署名検証もその場で試せます。

今すぐ試す →

TL;DR

1. 切り分けの順序 / Diagnostic order

  1. トークンを JWT Decoder で開く(payload と header を確認)
  2. header の alg と検証側の allow-list を比較
  3. header の kid を JWKS エンドポイントで解決できるか確認
  4. iss, aud が想定値か
  5. exp, nbf の時刻とサーバ時計の差(clock skew)

2. 原因別パターン / Cause table

症状 / Symptom原因 / Cause対処 / Fix
invalid signature鍵不一致 / Key mismatch発行側と検証側で同じ secret / public key を使う
invalid algorithmalg mismatchalgorithms: ['RS256'] で allow-list 固定
unable to find a signing key that matches 'kid'JWKS 未更新 / key rotationJWKS キャッシュをflush。発行側と検証側の期間を確認
jwt audience invalidaud 不一致検証側の audience オプションを実値と合わせる
jwt expiredexp切れ / clock skewNTPで時刻同期、clockTolerance: 5
invalid token (3分割失敗)文字列破損 / Base64URL崩れURLエンコード2重化、Bearer プレフィクスの削除漏れを確認

3. 言語別の検証コード / Verification in each language

Node.js (jsonwebtoken)

import jwt from "jsonwebtoken";
const payload = jwt.verify(token, publicKey, {
  algorithms: ["RS256"],   // allow-list 必須
  issuer: "https://example.com",
  audience: "api.example.com",
  clockTolerance: 5,
});

Python (PyJWT)

import jwt
payload = jwt.decode(
    token, public_key,
    algorithms=["RS256"],
    issuer="https://example.com",
    audience="api.example.com",
    leeway=5,
)

Go (github.com/golang-jwt/jwt)

tok, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
    if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
        return nil, fmt.Errorf("unexpected alg")
    }
    return publicKey, nil
})

4. セキュリティ上の注意 / Security notes

5. JWKSキャッシュと鍵ローテーションのタイミング問題 / JWKS caching and key rotation timing

JWKSエンドポイントのレスポンスは、毎回の検証で取得し直すとレイテンシが増えるため、 多くのクライアントライブラリが一定時間キャッシュします。鍵をローテーションした直後は、 新しいkidで署名されたトークンが届いても、検証側のキャッシュがまだ 古いJWKSを保持していてkidを解決できない、という失敗が起こり得ます。

6. 署名対象のバイト列が一致しているか確認する / Verifying the exact signed bytes

JWTの署名はbase64url(header) + "." + base64url(payload)という受け取った文字列そのものに対して計算されています。自前のJWT実装で、 headerやpayloadを一度オブジェクトへデコードしてから再エンコードし、 その再エンコード結果に対して署名検証をしてしまうと、JSONキーの並び順や空白の違いだけで バイト列が変わり、改ざんが無くても署名不一致になります。

// 危険な実装: デコード→オブジェクト化→再エンコードしてから検証
const [h, p, s] = token.split(".");
const header = JSON.parse(base64UrlDecode(h));
const payload = JSON.parse(base64UrlDecode(p));
const reEncoded = base64UrlEncode(JSON.stringify(header)) + "." + base64UrlEncode(JSON.stringify(payload));
verify(reEncoded, s); // 元のバイト列と一致しない可能性がある

// 正しい実装: 受け取った生のheader.payload文字列に対して検証する
const [h2, p2, s2] = token.split(".");
verify(`${h2}.${p2}`, s2);

標準的なJWTライブラリ(jsonwebtoken、PyJWT等)は内部で正しく生のセグメント文字列を 扱っているため、この問題は基本的に発生しません。自前でJWTの検証ロジックを書いている場合に 特有の落とし穴です。

7. English summary

"invalid signature" on a JWT almost always falls into five categories: key mismatch between issuer and verifier, algorithm mismatch (HS vs RS), unresolved kid after rotation, aud/iss mismatch, and exp/nbfdrift from clock skew. Always pin an algorithm allow-list on the verifier, refresh JWKS on rotation, and never trust the token's own alg header. For inspection, use a decoder to read header and payload without verifying, then compare against your verifier config.

よくある質問 / FAQ

invalid signature と signature verification failed の違いは?
両方とも署名とペイロードが一致しないことを意味します。ライブラリによって文言が違うだけで、原因は鍵不一致・alg不一致・改ざん・エンコード崩れのいずれかです。
What causes 'invalid signature' in JWT?
Either the signing key differs from the verification key, the algorithm differs, the token was tampered with, or the token string was corrupted (e.g. extra whitespace, URL-encoded twice).
HS256 と RS256 は混ぜて使えるか?
混ぜてはいけません。alg allow-listを検証側で固定し、トークンの alg ヘッダに依存しないこと。none 攻撃・alg 混乱攻撃の原因になります。

関連ツール / Related tools

関連ガイド / Related guides