SSRF・内部ネットワークアクセス・メタデータサービス
利用者が指定したURLの画像をサーバが取得し、プレビューを返す機能があります。攻撃者がそのURLを社内管理APIやクラウドのメタデータサービスへ向けたら、誰のネットワーク権限でアクセスするでしょうか。この記事では架空のプレビューAPIを通じ、SSRF(Server-Side Request Forgery)の成立条件、到達範囲、証拠と対策を追います。
科目B(午後)では単に『URLを検証する』で終えず、どのリダイレクト先・DNS解決結果・IP種別・ポートまで許すかを説明します。169.254.169.254はクラウド環境でメタデータサービスに使われる代表的なリンクローカル宛先です。以下の組織、ホスト、ログは教材用の架空例で、実際のクラウド環境の挙動は設定に依存します。
1. SSRFの主体と信頼境界
公開Webのhttps://portal.exampleにはPOST /api/previewがあります。利用者が指定した画像URLを、内部のプレビュー処理サーバ10.0.2.10が取得します。通常の宛先は画像CDNであるimg.partner.example(203.0.113.10)です。同じサーバからは内部管理API 10.0.0.5:8080と、環境によってはメタデータサービスへの経路があります。
用語・主体 | この例での意味 |
|---|---|
SSRF | 攻撃者がサーバのURL取得機能へ入力し、サーバ自身に意図しない宛先へのリクエストを出させる問題。攻撃者のブラウザが直接内部網へ接続する必要はない。 |
攻撃者が操作できる値 | POST /api/previewのurl値。URL文字列、パス、場合によっては攻撃者管理ドメインのDNS応答やリダイレクト応答を支配できる。サーバの内部IPを直接持つわけではない。 |
プレビュー処理サーバ | 10.0.2.10。サーバ自身のネットワーク到達性・認証情報を使ってURLを取得する。利用者にURLを表示するだけの処理ならSSRFにはならず、サーバから送信する段階が必要。 |
内部管理API | 10.0.0.5:8080。外部インターネットからは到達できなくても、プレビュー処理サーバから到達できる設定ならSSRFの対象になる。認証の有無と操作権限も別途確認する。 |
メタデータサービス | クラウド実行環境がインスタンス等へ設定・認証情報を提供する内部向け入口。169.254.169.254などを使う環境がある。攻撃成立には、その実行環境から実際に到達できることと、必要な認証手順を満たせることが要る。 |
ブラインドSSRF | サーバは意図しない宛先へ接続するが、取得した本文を攻撃者へ返さない形。応答本文が見えなくても、状態変更、内部ポート探索、DNSや外部への通知などの影響があり得る。 |
URLパーサとDNS | URLのホスト・スキーム・ポートを解釈し、名前をA/AAAAへ解決する。文字列で許可したホストと、実際に接続するIPが食い違うと境界を越える。 |
- 1. url値を送る
- 2. 正常な画像取得
- 3. 誤許可時の内部接続
- 4. 誤許可時のリンクローカル接続
攻撃者の入力を受けたプレビューサーバが、内部からHTTP要求を出す。
図の問題は、外部の利用者が内部網へ直接入ることではありません。url値をサーバが解釈し、プレビューサーバの権限で外向き接続を作ることです。内部側のFWやメタデータの認証設定が遮断していれば、URLが不正でも目的の本文取得には至りません。
2. 正常な取得と危険な取得を同じ順序で比較する
正常時はimg.partner.exampleの画像だけを取り込みます。APIは利用者の認可を確認し、urlを検証し、DNS解決結果を検証してから固定されたHTTPクライアントで取得します。返すのは画像の縮小版と処理結果だけで、取得先の任意の本文・ヘッダは返しません。
URL検証と実際の接続先IP検証を分けて行う。
DNSの名前だけを確認してから別のライブラリに再解決させると、検証時と接続時のIPが異なる可能性があります。接続に使うIPを検証済み結果へ固定するか、接続直前・リダイレクト後に実際の宛先を検証します。AだけでなくAAAAも確認します。
2-1. リクエストとサーバ側の判断点
POST /api/preview HTTP/1.1
Host: portal.example
Content-Type: application/json
{"url":"https://img.partner.example/p/42.png"}
判定1: scheme=https、host=img.partner.example、port=443
判定2: A=203.0.113.10、AAAAなし、許可IP範囲
判定3: redirect=なし、取得サイズ=420 KiB、type=image/png
結果: 縮小画像を保存し、利用者へ参照IDを返す判定点 | 確認する値と理由 |
|---|---|
業務上の入力 | そもそも任意URLが必要か判断する。提携画像IDを受けてサーバ側でURLを組み立てられるなら、任意URLの攻撃面をなくせる。 |
scheme・host・port | http/https以外や任意ポートを禁止し、許可した画像配信先だけを対象にする。URLパーサの結果を使い、文字列の前方一致だけで判定しない。 |
DNS解決結果 | A/AAAAのすべてと実際の接続先IPを確認し、ループバック、私設アドレス、リンクローカル、メタデータ宛先などを拒否する。許可ホスト名でも内向きIPなら接続しない。 |
リダイレクト | 原則無効にする。必要なら各Locationを新しいURLとして再検証し、回数を制限する。最初のURLだけを許可しても次の接続先まで安全とは言えない。 |
取得結果 | サイズ、時間、メディア型、ファイル構造を検証し、任意の応答本文や認証ヘッダを利用者へ返さない。ブラインドSSRFの副作用もあるので、本文を隠すだけでは解消しない。 |
出口制御 | プレビュー処理サーバから必要な宛先・ポートのみ外向き許可し、内部管理APIやメタデータ宛先をFW等で制限する。アプリの検証漏れがあっても到達を止める。 |
3. 内部APIとメタデータへの攻撃経路
攻撃者がurlをhttp://10.0.0.5:8080/admin/statusへ変えると、プレビューサーバが内部管理APIへアクセスを試みます。内部APIが送信元IPだけを信頼し、認証を省いていれば、プレビューサーバの位置を利用した操作が成立し得ます。APIが強い認証を要求し、プレビューサーバに資格情報がなければ、到達だけでは管理操作まで成功しません。
別の値としてhttp://169.254.169.254/への取得を考えます。クラウド環境ではリンクローカルのメタデータサービスが資格情報を提供する場合があります。ただしAWS EC2のIMDSv2のように、セッショントークン取得のためのPUTと後続のトークン付きGETが必要な構成では、単純なGETだけのSSRFで資格情報を取得できるとは限りません。
IMDSv2は重要な追加防御ですが、アプリが任意メソッド・任意ヘッダを送れるSSRFや、ほかの内部サービスを狙うSSRFを一律に無効化するわけではありません。メタデータエンドポイント自体を不要な実行環境から遮断し、実行ロールの権限も最小化します。資格情報を得た疑いがあれば、対象ロールの権限とAPI使用履歴を調べます。
3-1. メタデータで何が漏れるかを条件で判断する
メタデータサービスにはインスタンス識別や実行ロールに関する情報が含まれる場合があります。認証情報の取得が成立するには、プレビューサーバからそのエンドポイントへ到達し、サービスが要求するメソッド・ヘッダ・トークン手順を満たし、取得した本文が攻撃者の観測可能な位置へ戻ることが必要です。最初の二条件だけなら、まだ外部流出とは限りません。
攻撃者へ取得本文をそのまま返すプレビュー機能は、レスポンス型のSSRFになり得ます。一方、結果を返さない機能でも、内部APIへの状態変更や、攻撃者の受信先へ内部情報を含むリクエストを送る設計なら影響があります。『結果を返さない』だけで安全としない理由です。
観測段階 | 確定できることと追加証拠 |
|---|---|
URL入力を受理 | 攻撃者が指定値をアプリへ送れた。まだサーバからの接続は確認できない。 |
内部IPへ接続試行 | SSRFの送信段階に進んだ。FWが拒否したか、TCP接続が成立したかを分ける。 |
内部サービスが応答 | そのサービスへ到達した。認証成功、機密情報の返却、状態変更は応答内容と内部ログで確認する。 |
外部利用者に結果表示 | プレビューの出力が攻撃者に見えた。機密そのものが含まれるかは実際の本文を安全に確認する。 |
取得した資格の後続利用 | クラウドAPIの監査記録から実行ロールによる操作を追う。メタデータへの接続だけで後続のAPI悪用は断定しない。 |
3-2. 許可リストをすり抜ける条件
手口 | 検証をすり抜ける条件と防御 |
|---|---|
リダイレクト | 許可先img.partner.exampleが302で内部IPへ転送し、クライアントが自動追跡する。リダイレクトを止めるか全段で再検証する。 |
DNS再束縛・解決差 | 検証時は公開IP、接続時は内部IPを返す。名前の許可だけで終えず、実際に接続するA/AAAAを確認し、再解決経路を制御する。 |
URLの構文差 | userinfo、IPv6表記、エンコード、末尾ドット、IDNなどの解釈差で文字列チェックを欺く。信頼できるURLパーサで正規化したホスト・ポートを検証する。 |
プロキシ経由 | HTTPクライアントが環境変数のプロキシを使い、アプリが確認した接続先と実際の経路が変わる。プロキシ設定とCONNECT先、DNS実施主体を確認する。 |
別スキーム・別ポート | ライブラリがfile:などを受ける、又は管理サービスの8080へ接続できる。許可するスキームとポートを業務要件から限定する。 |
IPv6・リンクローカル | IPv4だけを拒否し、AAAAで内部・リンクローカル宛先へ接続する。IPv4とIPv6の両方を検査する。 |
- 1. 許可URLを入力:証跡 API入力と利用者/防御・対処 用途と許可先を制限
- 2. 302を返させる:証跡 上流のLocation/防御・対処 自動追跡を無効化
- 3. 内部IPへ接続:証跡 出口・内部APIログ/防御・対処 接続先IPを再検証
- 4. 応答や副作用:証跡 API応答・操作ログ/防御・対処 内部側も認証を要求
最初のURLだけを確認し、302のLocationを検証しない実装が必要条件。
図の第三段階でFWが内部管理APIへの接続を拒否していれば、攻撃者の目的は達しません。接続が成立しても、管理APIの認証が拒否すれば機密取得や操作は未成立です。入力値、サーバの接続試行、応答本文、内部サービスの実操作を段階ごとに確認します。
この図では、攻撃者が許可先の画像CDNでリダイレクト応答を作れる、又はそのCDN上に攻撃者が制御できる転送機能があると仮定します。許可先が固定の画像しか返さず、リダイレクトを一切出さないなら、この具体的な回避経路は成立しません。攻撃者がどの応答を支配できるかを条件に書きます。
3-3. DNS再束縛とURL文字列判定を詳しく見る
アプリが許可ホスト名を見てDNS解決し、公開IPであると確認した後、HTTPクライアントが同じホスト名をもう一度解決するとします。攻撃者がそのドメインのDNS応答を操作できれば、二度目に10.0.0.5のような内部IPを返す余地があります。検証済みIPを実際の接続へ固定しない設計が成立条件です。
URLの`@`より前に書かれた文字はuserinfoとして扱われ得ます。たとえば見た目に許可ホストが含まれていても、URLパーサが別ホストを接続先と解釈する形があります。文字列の`startsWith`や`includes`で判断せず、使用するHTTPクライアントと同じURL解釈結果を検証します。
CDNのIPは運用で変わるため、単一の公開IPを設定ファイルへ永久固定する設計が現実的でないこともあります。その場合でも、許可ドメインの管理主体、DNS応答、実際の接続先が公開アドレスか、プロキシ経由時の解決主体を分けて確認します。許可条件と運用変更の責任者を明記します。
4. 架空ログから事実と推測を分ける
次のログは同じ架空システムの異常アクセスです。時刻はUTC、URLとIPは記録を読みやすくした要約です。クラウドメタデータの本文や認証情報は記録していません。
11:00:00 app request_id=P7 user=guest
url=https://img.partner.example/redirect/42
initial_host=img.partner.example validation=allow
11:00:00 fetcher request_id=P7 status=302
location=http://10.0.0.5:8080/admin/status
11:00:01 egress request_id=P7
dst=10.0.0.5:8080 action=deny rule=internal-block
11:00:01 app request_id=P7 result=upstream_errorログ | 確定できること/まだ言えないこと |
|---|---|
initial_host=allow | 最初のURLは許可された。リダイレクト先の安全性を保証しない。 |
302 Location | 取得先が内部APIを指すLocationを返した。この時点だけではプレビューサーバが追跡したとは確定しない。 |
egress deny | プレビューサーバから内部APIへの接続が試みられ、出口制御で拒否された。内部APIが要求を処理したかは内部側ログも確認する。 |
upstream_error | 利用者へ正常なプレビューは返らなかった。攻撃が成立しなかったと断定する前に別の接続や副作用がないか調べる。 |
この例では出口FWの拒否が効いたことが主要な観測です。SSRFのあるアプリコードは残っている可能性が高いため、リダイレクト検証を修正します。『FWが止めたのでアプリ修正は不要』では、許可済みの別宛先や環境変更に弱いままです。
4-1. 送信元ログと宛先ログが一致しない場合
アプリにimg.partner.exampleへのURLしか残らず、出口FWには10.0.0.5:8080への接続がある場合は、リダイレクト、HTTPプロキシ、DNS解決差を調べます。最初のURLだけをアプリログへ記録する実装では、実際に接続した各ホップが見えません。要求ID、時刻、接続先IP、リダイレクト履歴を安全に記録します。
内部管理APIに10.0.2.10からのアクセスが残っても、それがプレビュー機能によるものか、正規バッチかは相関が要ります。内部APIは接続元IPだけで認証したことにせず、サービス間の認証と権限を持ちます。攻撃調査ではその認証結果と実行した操作まで確認します。
5. 実装・運用・復旧の条件
URL取得が必須かを再検討し、提携先IDや固定ホストで代替できるなら任意URL入力をなくす。
URLパーサでスキーム・ホスト・ポートを一度解釈し、正規化後に許可リストへ照合する。ユーザ名・パスワード付きURLや曖昧な構文は拒否する。
DNSのA・AAAAと実際の接続先を検査し、ループバック・私設・リンクローカル・メタデータ宛先を拒否する。リダイレクトは無効又は全段で再評価する。
ネットワーク出口ではプレビュー処理だけが必要な宛先へ出られるようにし、内部管理API側も認証と認可を要求する。
正常な画像取得、302で内部IPへ飛ぶURL、DNSが内向きIPへ変わるURL、IPv6、メタデータ宛先、長時間・大容量応答を試験する。
事故後は入力した利用者、到達を試みた内部IP、応答の有無、プレビュー結果に本文が含まれたかを調べます。メタデータ資格情報の流出が疑われる場合は、実行ロールの権限とAPI呼出し履歴を確認し、資格情報の無効化や権限変更を進めます。ブラインドSSRFなら結果本文がないことだけで無害としません。
再開条件は、アプリのURL検証、実際の接続先チェック、出口拒否、内部側認証が正常に働くことです。正当な画像プレビューが継続できることと、拒否すべき経路が遮断されることを同じリリースで確認します。
5-1. 障害・例外運用で境界を緩めない
提携先CDNが新しいIP範囲へ移動したとき、画像取得が失敗することがあります。その場で『すべての外向きIPを許可』に変更すると、内部IPやメタデータへの経路も広がり得ます。提携先の変更情報、DNS結果、必要な範囲を確認し、期限付きで変更します。
URL取得を別のワーカーやプロキシへ移した場合、内部管理APIへの到達性やメタデータの種類が変わります。元のサーバでSSRF対策が効いていても、新しい実行環境では同じ結果とは限りません。移行前後のネットワーク経路と資格情報を棚卸しし、拒否試験を繰り返します。
6. 科目B(午後)の記述演習
演習1:直接内部IPを指定
条件:url=http://10.0.0.5:8080/admin/statusを送った。アプリは取得を試み、FWは拒否した。問い:成立したことと未成立のことを分けよ。
解答:SSRF入力による内部宛先への接続試行は成立したが、FWが拒否したのでAPI応答取得は確認できない。誤答『内部データが必ず流出した』は拒否ログに反する。
演習2:302で検証を回避
条件:最初のURLは許可ホストだが、302 Locationが169.254.169.254を指す。問い:どこを追加検証するか。
解答:リダイレクトを無効にするか、Locationのスキーム・ホスト・ポート・解決後IPを再検証し、リンクローカル宛先を拒否する。誤答『初回URLが許可されたから次も安全』は別接続の宛先を見落とす。
演習3:IMDSv2の意味
条件:EC2でIMDSv2のトークン必須設定があり、プレビュー機能はGETしか送れない。問い:何が難しくなり、何が残るか。
解答:単純なGETだけでメタデータの資格情報を得ることは難しくなる。ただし内部API等へのSSRFや、別の送信機能による影響は残る。誤答『SSRF自体が消えた』は入口のURL取得処理を無視する。
演習4:DNS解決後のIP
条件:img.partner.exampleは許可ホストだが、接続時のAAAAが内部向けIPv6を返した。問い:判定に必要な値を述べよ。
解答:実際に接続するIPv6アドレスと許可範囲を照合し、内部・リンクローカルなら拒否する。誤答『ホスト名が許可リストにあるから通す』は接続先IPの変化を見逃す。
演習5:ブラインドSSRF
条件:プレビューAPIは取得本文を返さないが、GETで状態を変更する内部管理APIに副作用を起こした疑いがある。問い:調査する証跡を述べよ。
解答:プレビューサーバの送信先・メソッド、内部APIのアクセス・操作ログ、対象データの変更履歴を調べる。誤答『本文が返らないから被害ゼロ』は状態変更の可能性を無視する。
演習6:許可ホストの再解決
条件:URLの許可判定時は公開IPだが、HTTPクライアントの再解決時に10.0.0.5が返った。問い:必要な修正を述べよ。
解答:検証したIPと実際の接続先を一致させ、A/AAAAの全候補を確認し、内部アドレスへの出口も拒否する。誤答『ホスト名を許可したから安全』はDNS結果の変化を無視する。
7. 一次資料
OWASP:Server-Side Request Forgery Prevention Cheat Sheet
この記事についてAIに深掘り質問する
ChatGPT、Claude、Perplexityにこの記事を参照させ、要点の確認や疑問点を自由に質問できます。
次におすすめの学習
編集・検証について
編集・検証:IT資格ラボ編集部
IPAが公開する試験要綱・シラバス・過去問題と、各技術の公式資料を優先して内容を確認しています。制度変更や誤りを確認した場合は、記事を見直して更新します。
編集方針・情報源・訂正方針を見る