OpenID Connectの「署名検証」を甘く見るな:JWTという名のパンドラの箱
認証・認可の標準プロトコルとして、今やOpenID Connect (OIDC) はデファクトスタンダードだ。しかし、現場のコードレビューで私が最も多く目にする「致命的な落とし穴」は、驚くほど古典的な場所にある。それは、「IDトークン(JWT)の署名検証ロジック」だ。
多くのジュニアエンジニアは、ライブラリがよしなにやってくれるものだと信じている。だが、セキュリティアーキテクトの視点から言わせれば、ライブラリのデフォルト設定を理解せずに使うことは、鍵のかかっていない玄関に「警備中」の看板を掲げるのと同じだ。
1. 「none」アルゴリズムという悪夢の設計
JWT(JSON Web Token)の仕様には、歴史的な経緯から alg: "none" という、署名を強制しないアルゴリズムが存在する。これはデバッグ用やセキュアな閉域網内での利用を想定したものだが、悪意ある攻撃者にとって、これは「認証バイパス」への招待状だ。
もしあなたのアプリケーションが、受け取ったIDトークンの alg ヘッダを適切にチェックせず、署名の検証プロセスをスキップするような実装になっていれば、攻撃者は以下のようなトークンを生成し、管理者になりすます。
// 悪意のあるペイロード例
{
“alg”: “none”,
“typ”: “JWT”
}
{
“sub”: “admin-user-id”, // 管理者IDに書き換え
“iss”: “trusted-issuer”,
“exp”: 1999999999
}
このトークンをBase64で結合し、末尾にドット(.)を付与して送信するだけで、多くの脆弱な検証ロジックをすり抜けることが可能だ。
2. ライブラリ選定と実装の鉄則
「署名アルゴリズムを強制する」とは、単にライブラリの関数を呼ぶことではない。「期待されるアルゴリズム(例: RS256, ES256)以外をホワイトリスト形式で弾く」という厳格なガードレイルを敷くことだ。
以下は、Node.jsのライブラリ(joseなど)を用いた、セキュアな検証の基本実装例である。
import { jwtVerify, createRemoteJWKSet } from ‘jose’;
// JWKSエンドポイントから公開鍵を動的に取得する(ハードコードは厳禁)
const JWKS = createRemoteJWKSet(new URL(‘https://auth.example.com/.well-known/jwks.json’));
async function verifyIdToken(jwt) {
try {
// 【重要】algを明示的に指定して検証を行う
// これにより、noneアルゴリズムや意図しない暗号アルゴリズムを確実に拒否する
const { payload } = await jwtVerify(jwt, JWKS, {
issuer: ‘https://auth.example.com’,
audience: ‘my-client-id’,
algorithms: [‘RS256’] // 許可するアルゴリズムを厳格に指定
});
return payload;
} catch (err) {
// 検証失敗時は即座にログを吐き、セッションを破棄する
console.error(‘JWT検証エラー:’, err.message);
throw new Error(‘Unauthorized’);
}
}
3. 深層防御:耐量子時代を見据えたID基盤
今後、RSAやECDSAといった現在の公開鍵暗号は、量子コンピュータの台頭(Shorのアルゴリズム)によって脅かされるリスクがある。OIDCの実装においても、今後は「耐量子暗号(PQC)への移行パス」をアーキテクチャ設計に盛り込む必要がある。
今すぐ全ての署名をPQCに変える必要はないが、少なくとも以下のポイントは今のうちから意識しておくべきだ。
- 鍵のローテーション戦略: 公開鍵の変更に強いAPI設計にしておくこと。
- ヘッダの厳密なバリデーション:
kid(Key ID) を利用し、サーバー側で管理する公開鍵セットを定期的にリフレッシュする仕組みを確立すること。 - プロンプトインジェクションとの交差点: IDトークンのクレーム(
subやemail)をそのままLLMのコンテキストに渡すのは非常に危険だ。トークンの検証を終えた後も、必ず「入力値のサニタイズ」と「LLMのガードレイル(入力の無害化)」を二重に適用せよ。
最後に:セキュリティは「性悪説」で構築せよ
現場でインシデントハンドリングをしていると、決まって「まさかこのトークンが偽造されているとは……」という声を聞く。しかし、ネットワークの端から端まで、信頼できるものは一つもないと考えたほうがいい。
IDトークンの署名を検証することは、アイデンティティの根源を担保する行為だ。脆弱なライブラリの設定を放置し、攻撃者が「none」アルゴリズムで扉をこじ開けるのを指をくわえて待つのか。それとも、アーキテクチャの根幹から堅牢性を高めるのか。
決めるのは、今これを読んでいる君だ。技術的な負債を「仕様」と呼ぶのは今日で終わりにしよう。
コメント