エラーメッセージ
Signed JWT rejected: Invalid signature
別の表記
- com.nimbusds.jose.proc.BadJWSException: Signed JWT rejected: Invalid signature
- org.springframework.security.oauth2.jwt.BadJwtException: An error occurred while attempting to decode the Jwt: Signed JWT rejected: Invalid signature
Nimbus JOSE + JWT (Java) — BadJWSException from DefaultJWTProcessor; also surfaced by Spring Security's NimbusJwtDecoder
このエラーの意味
Nimbus は、`kid`、`alg`、鍵の種類に基づいてトークンの候補となる鍵を 1 つ以上選択しましたが、どの鍵でも署名を検証できませんでした。鍵の選択には成功しています(失敗していれば「Another algorithm expected, or no matching key(s) found」と表示されます)。つまり、信頼している鍵とトークンに署名した鍵が異なるか、トークンが改変されています。Spring Security は同じ状態を `NimbusJwtDecoder` を通じて報告し、先頭に「An error occurred while attempting to decode the Jwt:」を付けます。
発生する理由
kid は同じでも鍵が異なる
よくある`default`、`rsa1`、日付のような鍵 ID は、鍵を再生成したとき(環境ごと、デプロイごと、古い鍵を紛失した後など)に再利用されます。検証者は正しい `kid` を持つ鍵を見つけますが、その鍵がトークンに署名したことはありません。
リソースサーバーが別の発行者の鍵を信頼している
よくある`jwk-set-uri` が、トークンを発行しているものとは別のレルム、テナント、環境を指しているケースです。
サービス間で HMAC シークレットが異なる
ときどきHS256 と `MACVerifier` を使う場合、共有シークレットのバイト列に少しでも違い(エンコーディング、末尾の改行)があると、すべての署名が無効になります。
署名後にトークンが変更された
ときどきプロキシによってヘッダーやペイロードが再エンコードされた、あるいは別々のトークンの一部を組み合わせてトークンが作られた、といったケースです。
修正方法
- 1.
手作業でコピーした鍵セットではなく、発行者を設定する
`issuer-uri` を指定すると、Spring Security は発行者のディスカバリドキュメントから `jwks_uri` を読み取り、`iss` も検証します。
yaml # Spring Boot resource server: derive the JWK Set from the issuer's discovery document spring: security: oauth2: resourceserver: jwt: issuer-uri: https://issuer.example.com/realms/main - 2.
kid だけでなくサムプリントも比較する
RFC 7638 のサムプリントは、鍵の実体そのものを識別します。公開されている鍵のサムプリントが、発行者が署名に使っている鍵のサムプリントと異なる場合、その `kid` は再利用されています。
java SignedJWT jwt = SignedJWT.parse(token); System.out.println(jwt.getHeader().getKeyID() + " " + jwt.getHeader().getAlgorithm()); JWKSet set = JWKSet.load(new java.net.URI("https://issuer.example.com/.well-known/jwks.json").toURL()); for (JWK key : set.getKeys()) { System.out.println(key.getKeyID() + " " + key.getAlgorithm() + " thumbprint=" + key.computeThumbprint()); }