IDトークンは「鍵」じゃない、「身分証明書のコピー」だ!OIDCの落とし穴を徹底解説
こんにちは。日々、システムの守りを固めるエンジニアの皆さん、お疲れ様です。
今日は、Webサービスのログイン処理で当たり前のように使われている「OpenID Connect(OIDC)」の、ちょっと危ない盲点についてお話しします。
「IDトークンさえ受け取ればログイン成功でしょ?」と思っているとしたら……残念ながら、そのシステムは泥棒に正面玄関を全開にしているのと同じかもしれません。
今回は、特に新人のエンジニアさんがやってしまいがちな「IDトークンの検証不備」という、セキュリティの世界では定番にして致命的なミスについて、身近な例えを交えて紐解いていきましょう。
—
1. IDトークンって何者?:家の鍵と「身分証明書」の違い
まず、頭の中を整理しましょう。IDトークンは「鍵」ではありません。「信頼できる機関(認証サーバー)が発行した、顔写真付きの身分証明書」です。
あなたがマンションの管理人(Webサーバー)だとします。
ある人が「私は住民のAさんです!」と言って、身分証明書を見せてきました。
- もし身分証明書が本物なら: 部屋に通してあげますよね。
- もし身分証明書が偽造品だったら: 泥棒が「Aさんです」と嘘をついて侵入してしまいます。
ここで重要なのは、「身分証明書が本物かどうか(改ざんされていないか)」を、管理人がその場でチェックする義務があるという点です。これを怠るのが、今回取り上げる「検証不備」という脆弱性です。
—
2. 攻撃者はどうやって「偽造」するのか?
OIDCのIDトークンは、実はただのテキストデータ(JWT: JSON Web Token)です。中身は誰でも読めます。
攻撃者は、このトークンの中身を書き換えて「管理者権限」や「別のユーザーのID」にすり替えます。
ここで登場するのが「署名(Signature)」です。
身分証明書の裏面に、発行元が「この証明書は本物です」というハンコ(電子署名)を押しています。このハンコのおかげで、少しでも内容が書き換えられたら「ハンコが一致しない!」とすぐに見抜けます。
狙われる「noneアルゴリズム」という罠
ところが、世の中には「none」アルゴリズムという、一見便利な(しかし恐ろしい)仕組みがあります。これは「署名なんて面倒だからチェックしなくていいよ!」という設定です。
もしあなたのシステムが「none」を許可していたら、攻撃者はこうします。
1. 署名部分を削除する。
2. 中身を「自分は管理者です」と書き換える。
3. アルゴリズムを「none」に設定して送信する。
すると、プログラムは「署名チェック?『none』って書いてあるからスキップしよっと!」と、偽造された証明書をそのまま受け入れてしまうのです。これがなりすましのメカニズムです。
—
3. どうやって防ぐ?:泥臭い検証のルール
では、どうすればいいのでしょうか。答えはシンプルです。「信頼できるライブラリを使い、設定を厳しくする」こと。
実践:正しい検証のコード例(Node.js/jsonwebtokenの例)
多くの言語には、この面倒な検証を肩代わりしてくれるライブラリがあります。重要なのは「noneを無効化し、公開鍵を正しく指定すること」です。
const jwt = require(‘jsonwebtoken’);
// 認証サーバーから取得した「公開鍵」
const publicKey = -----BEGIN PUBLIC KEY-----\n...(ここに公開鍵)...\n-----END PUBLIC KEY-----;
try {
// IDトークンを検証する
const decoded = jwt.verify(token, publicKey, {
algorithms: [‘RS256’], // ここが重要!「none」を一切許可せず、特定のアルゴリズムのみを強制する
issuer: ‘https://accounts.example.com’, // 発行元が正しいかチェック
audience: ‘my-client-id’ // 自分のサービス宛てに発行されたものかチェック
});
console.log(“ログイン成功!”, decoded);
} catch (err) {
// ここに到達するということは、署名が違うか、改ざんされているということ
console.error(“不正なトークンです!アクセスを拒否します。”, err.message);
}
この設定のポイント
algorithms: ['RS256']: これを指定することで、noneやその他の怪しいアルゴリズムを門前払いできます。「RS256しか認めない!」と宣言するのが鉄則です。- issuer/audienceの検証: 「誰が発行したか」「誰のために発行したか」を確認することで、他のサービスのトークンを使い回す攻撃を防ぎます。
—
4. 今日からできるアクションプラン
最後に、現場で明日からできるチェックリストをお伝えします。
1. 「none」を絶対に使わない: どんなに開発が忙しくても、アルゴリズム指定に none を入れてはいけません。
2. ライブラリのドキュメントを読み直す: 今使っている認証ライブラリが「デフォルトで署名を検証してくれるのか?」を確認してください。
3. 公開鍵をハードコードしない: 認証サーバーが提供する jwks_uri から動的に公開鍵を取得するようにしましょう。鍵は定期的に更新されるからです。
セキュリティ対策は、一度やって終わりではありません。ですが、こうして仕組みを一つずつ理解していけば、決して難しいことではありません。
「認証はシステムの心臓部」。ここをしっかり守ることで、ユーザーが安心して使えるサービスを一緒に育てていきましょう。一歩ずつ、着実に。それが、最強の防御への近道ですよ!
コメント