エラーメッセージ
Error(InvalidSignature)
別の表記
- called `Result::unwrap()` on an `Err` value: Error(InvalidSignature)
- InvalidSignature
`{:?}` は `Error(InvalidSignature)` を、`{}` は `InvalidSignature` を出力します。`.unwrap()` は「called `Result::unwrap()` on an `Err` value: Error(InvalidSignature)」でパニックします。
jsonwebtoken (Rust crate) — ErrorKind::InvalidSignature from decode()
このエラーの意味
`jsonwebtoken` クレートはトークンを解析してそのアルゴリズムを受け入れましたが、渡された `DecodingKey` では署名の検証に失敗しました。`Error(InvalidSignature)` はログや `unwrap()` のパニックで目にする `Debug` 形式で、`Display` 形式では単に `InvalidSignature` です。このクレートは「鍵の誤り」と「トークンの改変」を区別しません。どちらも、この鍵に対して署名が有効ではないことを意味します。
発生する理由
DecodingKey が署名者の鍵ではない
よくある別の環境の鍵、発行者がローテーションした後もハードコードされたままの鍵、または `kid` ではなく位置で JWK を選んでいるケースです。
トークンの ECDSA 署名が DER エンコードされている
ときどきKMS や OpenSSL の署名を使って手作業で組み立てたトークンには、ES256 が要求する 64 バイトの r‖s ではなく DER(約 71 バイト)が入っているため、検証に成功することはありません。
HS256 のシークレットのバイト列が異なる
ときどき一方の側で `DecodingKey::from_secret(b"...")`、もう一方の側で `from_base64_secret("...")` を使うと、同じ設定文字列から異なる鍵が生成されます。
修正方法
- 1.
エラーの種類を明示的に処理する
`ErrorKind::InvalidSignature` を 401 に対応付け、有効期限切れとは区別してください。そうすれば、クライアントは決して検証を通らないトークンで再試行しなくなります。
rust use jsonwebtoken::errors::ErrorKind; match decode::<Claims>(&token, &key, &validation) { Ok(data) => Ok(data.claims), Err(e) => match e.kind() { ErrorKind::InvalidSignature => Err(AuthError::Unauthorized), // wrong key or tampered token ErrorKind::ExpiredSignature => Err(AuthError::Expired), _ => Err(AuthError::BadToken), }, } - 2.
発行者の JWKS から kid で鍵を選択する
`JwkSet::find` と `DecodingKey::from_jwk` を使うと、発行者がヘッダーで指定した鍵を正確に選択できます。
rust use jsonwebtoken::{decode_header, jwk::JwkSet, DecodingKey}; let header = decode_header(&token)?; let kid = header.kid.ok_or(AuthError::BadToken)?; let jwk = jwks.find(&kid).ok_or(AuthError::UnknownKey)?; // jwks: JwkSet fetched from the issuer let key = DecodingKey::from_jwk(jwk)?;