OIDCにおけるセキュリティ対策が、脅威をブロックしている方法
OIDCのフローにおいては、セキュアな情報を盗まれてしまう懸念点があります。そのためにセキュアなプロトコルが存在しています。セキュリティ対策として何ができるのかをそれぞれ見ていきます。
OIDCフローのシーケンス
OIDCの簡略シーケンスを下記として定義します。
sequenceDiagram
participant User
participant UserAgent
participant RP as Relying Party (Client)
participant OP as OpenID Provider (Authorization Server)
User ->> UserAgent: ①:アプリにアクセス
UserAgent ->> RP: ②:認証が必要なリソースにアクセス
RP ->> RP: ③-1:state を生成
RP ->> RP: ③-2:code_verifier を生成
RP ->> RP: ③-3:code_challenge = BASE64URL-ENCODE(SHA256(code_verifier))
RP ->> UserAgent: ④:OPへのリダイレクト指示(/authorize?\n scope=openid\n response_type=code\n client_id=...\n redirect_uri=...\n state=xxx\n code_challenge=xxx\n code_challenge_method=S256)
UserAgent ->> OP: ⑤:認可リクエスト送信
OP ->> OP: ⑥-1:client_id, redirect_uri, scope 等の検証
OP ->> User: ⑥-2:ログイン&同意画面
User(UserAgent) ->> OP: ⑦:認証 & 同意
OP ->> UserAgent: ⑧:リダイレクト(redirect_uri?code=xyz&state=xxx)
UserAgent ->> RP: ⑨:認可コードとstateを送信
RP ->> RP: ⑩:stateを検証
RP ->> OP: ⑪:トークンリクエスト(/token\n grant_type=authorization_code\n code=xyz\n redirect_uri=...\n client_id=...\n code_verifier=元の値)
OP ->> RP: ⑫:IDトークン + アクセストークン
RP ->> RP: ⑬:IDトークン検証(ユーザー認証完了)
RP ->> UserAgent: ⑭:トークンの付与
セキュリティコードの必要性
state
発行者:RP
検証者:RP
脅威:CSRFの脅威
OP ->> UserAgent: ⑧:リダイレクト(redirect_uri?code=xyz&state=xxx)する際に、RPは、OPから返ってきた認可コード(code)とstateが「自分が開始したリクエストに対する応答かどうか」を検証しないと、外部からの偽リクエストを処理してしまう可能性がある。
対策:
RP自身が発行したstateを事前に保存しておき、リダイレクト応答で受け取ったstateと一致するかを検証することで、「この認可応答が自分自身によるリクエストの結果である」ことを確認し、CSRFを防止する。
攻撃の流れ(state がない想定):
- 攻撃者は自分のブラウザでOPにログインして、自分用の認可コードを取得
- redirect_uri?code=attackerCode というURLを生成
- このURLをターゲット(被害者)に踏ませる(例:メールやサイトにリンクを埋め込む)
- ターゲットのブラウザがそのURLにアクセス
- RPは state 検証をしないので code=attackerCode を信用してトークン取得
結果:被害者のセッションが攻撃者のアカウントでログインされる
CSRFの脅威
client_id(redirect_uri)
発行者:OPが発行(RPがredirect_uriの登録を申請し、OPがredirect_uriを登録、client_idを発行)
検証者:OP
脅威:
利用を許可していないredirect_uriにリダイレクトすること
対策:
事前に利用するRPのclient_id・redirect_uriをOPは保持しておきます。そうすることで、利用を許可していないredirect_uriにリダイレクトさせることを防ぎます。
code_verifier, code_challenge / PKCE
発行者:RP
検証者:OP(/token
リクエスト時に code_verifier を使って検証する)
脅威:code injection attack
認可コードが中間者や悪意のあるアプリに盗まれた場合、そのコードを使ってトークンを取得されてしまう(code injection attack)に、無条件にtokenを発行してしまう。悪意のあるRPを判断することができない。
対策:
RPが事前に作成したcode_challenge、code_verifierのペアとなるデータを利用する。
> ④:OPへのリダイレクト指示(/authorize?\n scope=openid\n response_type=code\n client_id=...\n redirect_uri=...\n state=xxx\n code_challenge=xxx\n code_challenge_method=S256)
認可リクエスト時にcode_verifier・code_challenge_methodを送り
> ⑪:トークンリクエスト(/token\n grant_type=authorization_code\n code=xyz\n redirect_uri=...\n client_id=...\n code_verifier=元の値)
トークンリクエスト時にcode_verifierを送ることによって、認可リクエストを開始したRPとトークンリクエストしたRPが同一リソースであることを判定する。
codeがインジェクションされる可能性
codeを盗まれてしまうケースについて。
悪意あるリダイレクトURIへの送信(open redirect + client misconfiguration)
脅威:
攻撃者が不正な redirect_uri を OP に送り、そこに code を送らせることで code を盗む
対応:
OP 側で redirect_uri を完全一致で検証し、client_id ごとに事前登録された redirect_uri 以外は拒否
リダイレクト中の通信がHTTPSでない(中間者攻撃 / MITM)
脅威:
ネットワーク上で暗号化されてない平文でcodeが送られてしまう
対応:
redirect_uri を https に限定する(OP 側のポリシーでも enforce)+ トークンエンドポイントも含めすべて https にする
ログ / ブラウザ履歴 / リファラからの漏洩
脅威:
リダイレクトURLに含まれる code
が、サーバーのアクセスログ、ブラウザの履歴、Referer ヘッダーなどに残り、他のスクリプトや中間者に読み取られる可能性がある
対策:
state と PKCE を併用
WebViewやカスタムURLスキーム使用時の誤実装
脅威:
モバイルアプリでは、WebViewや myapp://callback?code=xyz のようなカスタムスキームでリダイレクトを受けるケースがある。誤って複数アプリがそのスキームを登録していると、攻撃者アプリが横取りする可能性もある
対策:
カスタムスキームではなく OS 提供の App-claimed URI(例:App Links / Universal Links)を使う
state と PKCE を併用
Discussion