セキュリティ

SSRFとは?仕組み・クラウドメタデータ悪用と防御実装を解説

Adureを利用したインフラ構築

SSRF(Server Side Request Forgery)は、サーバーに「本来アクセスできないはずの宛先」へ代理でリクエストを送らせる脆弱性です。ブラウザが攻撃の起点になるCSRFとは違い、狙われるのはサーバーが持つ内部ネットワークへの到達性と、クラウドから払い出された一時的な認証情報になります。この記事では、成立の前提条件、クラウドで被害が跳ね上がる構造、IMDSv2で防げる範囲と残る抜け道、拒否リストではなく宛先の許可リストで守る実装までをコード例つきで整理しました。OWASP Top 10:2025で扱いが変わった点にも触れます。

まとめ|SSRF防御で先に決める三つの実装方針

結論から示します。第一の方針は、入力されたURLを検査するのではなく、接続してよい宛先そのものを許可リストで固定することです。攻撃者が使えるURL表記は十進数のIP、IPv6、@記法、短縮URL、リダイレクトと際限なく増えるため、危険な文字列を数え上げて弾く発想は必ず破られました。守るべきは「どこへ出てよいか」の側になります。

第二の方針は、HTTPクライアントにリダイレクト追従を任せないことです。多くのライブラリは既定で3xxを自動的に追いかけます。入口のURLだけ検証しても、リダイレクト先が内部アドレスへ差し替わればそこで検証は無効化されてしまう。追従を切り、一段ごとに宛先を検証し直す実装へ変えてください。

第三の方針は、アプリケーションの外側で出口を絞ることでした。コードの検証は必ずどこかで抜けが出る前提に立ち、送信元から内部セグメントとメタデータへ到達できない経路にしておく。以降の章では、この三つを実装へ落としていきます。

SSRFの仕組みと、攻撃が成立するために必要な三つの前提条件

SSRFは特殊な攻撃ではなく、ごく普通の機能の裏返しとして現れます。まず成立の条件から押さえます。

利用者が指定したURLへサーバーが代理でリクエストを送る処理が起点

SSRFの起点は、利用者が渡した値をもとにサーバー側がHTTPリクエストを組み立てる処理です。URLをそのまま受け取る場合だけでなく、ホスト名やIDの一部を受け取って文字列連結でURLを組む場合も同じ扱いになります。

攻撃者から見た手順は単純でした。フォームやAPIに http://169.254.169.254/ のような内部向けのURLを入れ、サーバーがそこへ接続するかどうかを観察する。サーバーはインターネット側からは届かない領域にいるため、攻撃者は自分の手が届かない範囲へ、サーバーを踏み台にして手を伸ばせます。脆弱性そのものの分類や管理の流れは脆弱性とは何かを整理した解説にまとめてあります。

見落としがちなのは、入力欄が「URL」の形をしていないケースです。ホスト名だけを受け取るヘルスチェック、地域コードから内部エンドポイントを組み立てる処理。文字列連結の途中に攻撃者の値が入るなら、そこもSSRFの入口になり得ます。

応答が返るBasic SSRFと応答が返らないBlind SSRFの違い

SSRFは応答の見え方で二種類に分かれ、対処の優先度も変わります。

Basic SSRF(応答返却型)では、サーバーが取得した内容がそのまま画面やAPIレスポンスに返ります。攻撃者は内部の管理画面やメタデータの中身を直接読めるため、情報漏えいへ一直線に繋がりました。プレビュー機能やURL取り込み機能はこの型になりやすい構造です。

Blind SSRF(応答非返却型)では、リクエストは飛ぶものの内容は返りません。ただし無害ではなく、応答時間の差から内部ポートの開閉を推測でき、GETで副作用のある内部APIを叩けば状態を書き換えられます。診断で見つけにくいだけで、危険度が下がるわけではない点に注意してください。

SSRF攻撃が刺さる前提は内部からしか到達できない資産の存在

三つ目の前提は、サーバーの位置からしか届かない資産があることです。これが無ければSSRFは「外部サイトを代理で取得するだけの機能」で終わります。

実際に狙われる資産は次のあたりに集中しています。

狙われる資産 代表的な宛先 攻撃者の目的
クラウドのメタデータ 169.254.169.254 一時認証情報の窃取
同一ホストの内部API 127.0.0.1 の各ポート 認証を迂回した操作
同一VPCの管理画面 10.0.0.0/8 など 横展開と権限昇格
社内の名前解決 内部専用ホスト名 構成情報の探索

逆に言えば、この表の宛先へアプリケーションサーバーから到達できない状態を作れば、SSRFが見つかっても被害は限定されました。

クラウド環境でSSRFの被害が跳ね上がる構造的な理由と侵入経路

SSRFが2010年代後半から扱いを変えられたのは、クラウドの認証設計が理由でした。オンプレミスの時代と何が違うのかを整理します。

インスタンスメタデータから一時的な認証情報が読み出される経路

クラウドの仮想マシンは、リンクローカルアドレス 169.254.169.254 上のメタデータサービスから自分自身の情報を取得できます。ここにはインスタンスIDやネットワーク構成に加えて、割り当てられたロールの一時認証情報も含まれていました。

つまりSSRFでこのアドレスへ到達できると、攻撃者はそのインスタンスの権限をそのまま手に入れます。ロールにストレージやデータベースへの広い権限が付いていれば、被害はアプリケーションの外へ一気に広がる。SSRFがOWASPで単独項目として立てられた背景にも、この構造がありました。仮想マシン側の仕組みはAmazon EC2の仕組みと採用判断の解説で確認できます。

付け加えると、OWASP Top 10:2025ではSSRF(CWE-918)が独立項目ではなく、A01 Broken Access Control の収載CWEとして扱われるようになりました(2026年8月時点の公式掲載)。「サーバーが本来アクセスを許されていない内部資源へ到達してしまう認可の失敗」という位置づけです。版ごとの項目の入れ替わりはOWASP Top 10 2025年版の変更点にまとめてあります。

IMDSv2が防げる範囲と、それでも残る二つの抜け道について

この経路への対策として提供されたのがIMDSv2です。仕組みは三点あります。トークンをPUTで先に取得すること、取得したトークンを専用ヘッダに載せること、そしてホップ数の上限(既定は1)でプロキシ越しの中継を弾くこと。

TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" \
  -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
  "http://169.254.169.254/latest/meta-data/"

単純なGETしか送れない典型的なSSRFは、この時点で止まります。PUTを送れず、任意ヘッダも付けられないためです。AWS側も2024年3月にリージョン単位で新規起動をIMDSv2既定にする設定を用意し、2024年半ば以降に登場したインスタンスタイプはIMDSv2のみへ寄せられました。

ただし抜け道は二つ残ります。ひとつは、HTTPメソッドと任意ヘッダを操作できるタイプのSSRFです。プロキシ機能やgopherスキーマ経由でリクエストを丸ごと組み立てられる実装では、トークン取得そのものを攻撃者が実行できてしまう。もうひとつは既存インスタンスで、既定設定の変更は新規起動にしか効かず、稼働中のものはIMDSv1が有効なまま残ります。棚卸しと個別の切り替えが要りました。

コンテナとサーバーレス環境で異なるメタデータの露出面と注意点

実行基盤が変わると、露出する面も変わります。コンテナでは、タスクやポッドに割り当てられた認証情報の取得口が別に用意されており、そちらは環境変数で渡されるエンドポイントを通る形でした。ホップ数制限だけを頼りにすると、この経路が抜けます。

サーバーレス関数では認証情報が環境変数として渡る設計が一般的で、メタデータへのHTTPアクセスという形はとりません。その代わり関数から外部への通信が既定で自由なことが多く、内部のAPIゲートウェイやデータベースのプロキシへ到達される余地が残ります。どの基盤でも「何が、どこから認証情報を受け取っているか」を先に洗い出してください。

SSRFが混入しやすい実装パターンと見落としやすい入力の入口

設計レビューで先に見るべき箇所は、経験上ほぼ決まっています。三つの型に分けて示します。

URLを受け取る機能はプレビューとWebhookと画像取得に集中

最も分かりやすい入口は、URLを受け取ることが機能そのものになっている処理です。リンクのOGP取得、Webhookの送信先登録、外部画像のインポート、RSSの購読、フィード連携、ヘッドレスブラウザによるスクリーンショット生成。この六つは、まず疑ってよい対象になります。

なかでもWebhookは扱いが難しい機能でした。任意のURLを登録できること自体が仕様の中心にあり、許可リストで固定する定石が使えません。この型は後述の出口制御と、送信元を分離した専用ワーカーで受け止めます。

ファイルの取り込みや文書変換に潜む間接的なURL解決の危うさ

二つ目は、URLが入力欄に現れない型です。アップロードされたファイルの中身をパーサが解釈する過程で、外部参照が解決されてしまう経路があります。

典型例を挙げます。XMLの外部実体参照、SVG内の画像参照やxlink、HTMLをPDFへ変換するライブラリが読み込むリモートのCSSやフォント、Markdownの画像URL、オフィス文書の外部データ接続。いずれも「利用者はファイルを上げただけ」に見えるのに、サーバーは任意の宛先へ接続します。ファイル変換の処理系を導入するときは、外部参照を無効化する設定があるかを先に確認しておきましょう。

URLスキーマとリダイレクトを介した検証回避の代表的な手口五つ

三つ目は、検証コードがあるのに回避される型です。よく使われる手口を並べます。

  • 非HTTPスキーマの悪用(file、gopher、dict、ftp などを渡してプロトコルを乗り換える)
  • アドレス表記の言い換え(十進数表記の2130706433、8進数表記、IPv4射影のIPv6アドレス)
  • ホスト名の細工(内部IPを解決する公開DNS名、@記法での認証情報部分への埋め込み)
  • リダイレクトの多段化(許可されたドメインから3xxで内部アドレスへ飛ばす)
  • DNSリバインディング(検証時は外部IP、接続時は内部IPを返すよう応答を切り替える)

この一覧を「全部弾くコードを書けばよい」と読むと、次に見つかる表記でまた破られます。手口の数え上げは防御の主軸に置かず、検証コードのテストケースとして使ってください。

SSRF防御の実装は拒否リストではなく宛先の許可リストで設計する

ここからが実装の中心です。設計の順序を四段階で示します。

許可リストはホスト名ではなく名前解決後のIPアドレスで判定する

まず、接続先として許すホストを列挙します。そのうえで、ホスト名の一致だけで通してはいけません。攻撃者が管理するDNSレコードは内部IPを返せるため、名前解決した結果のIPアドレスを見て内部レンジを拒否する判定を重ねます。

import ipaddress
import socket

ALLOWED_HOSTS = {"api.partner.example.com"}

def resolve_allowed(host, port):
    if host not in ALLOWED_HOSTS:
        raise ValueError("host not allowed")
    infos = socket.getaddrinfo(host, port, proto=socket.IPPROTO_TCP)
    ips = {info[4][0] for info in infos}
    for ip in ips:
        addr = ipaddress.ip_address(ip)
        if (addr.is_private or addr.is_loopback
                or addr.is_link_local or addr.is_reserved):
            raise ValueError("internal address blocked")
    return ips

拒否すべきレンジは、プライベート・ループバック・リンクローカル・予約済みの四種類が基本です。IPv6も同様に判定してください。IPv4しか見ていない実装は、IPv4射影やユニークローカルのアドレスで抜けられました。

名前解決と接続の間で宛先が変わるDNSリバインディングの対処

前節のコードには、そのままでは残る穴があります。検証で名前解決した結果と、実際にHTTPクライアントが接続するときの解決結果が別になり得るからです。極端に短いTTLで応答を切り替えられると、検証は外部IPで通り、接続は内部IPへ向かいます。

実装上の解決策は二つです。ひとつは、検証で得たIPアドレスへ直接接続し、Hostヘッダとサーバー名表示で本来のホスト名を伝える方法。もうひとつは、ソケット接続の直前に呼ばれるフックを用意し、実際に接続しようとしている宛先アドレスをそこで検証する方法になります。前者は実装が読みやすく、後者はライブラリの層で一律に効かせられる利点がありました。

HTTPクライアントのリダイレクト追従を切って一段ずつ検証する

三段階目はリダイレクトです。既定で追従するクライアントは多く、そのままでは入口の検証を通した後に宛先を差し替えられます。

resp = session.get(url, allow_redirects=False, timeout=(3, 5))
hops = 0
while resp.is_redirect and hops < 3:
    next_url = resp.headers["Location"]
    validate_destination(next_url)
    resp = session.get(next_url, allow_redirects=False, timeout=(3, 5))
    hops += 1

追従の上限を設けたうえで、毎回 validate_destination を通し直すのが要点です。あわせて、接続タイムアウトと読み取りタイムアウトを短めに設定してください。Blind SSRFで内部ポートの開閉を推測する手口は応答時間の差を使うため、タイムアウトを揃えておくと得られる情報が減ります。

ネットワーク層で通信の出口を絞り込む多層防御の構成と運用手順

四段階目は、コードの外側です。アプリケーションからの外向き通信を既定で拒否し、必要な宛先だけを通す構成にします。egressの制御をセキュリティグループやファイアウォールで行い、外部への取得処理はプロキシを1台経由させて、そのプロキシから内部セグメントへ戻れないようにする。この形にしておくと、検証コードに漏れがあっても被害が止まります。

入口側ではWAFのシグネチャで内部アドレスを含むリクエストを弾く運用も併用できます。ただし表記の言い換えで回避されるため、主たる防御線には数えないでください。境界防御の位置づけと選び方はWAFの仕組みと選び方の解説で整理しています。

SSRF対策をどこまで実装するかの判断基準と見送ってよい条件

ここが本記事の結論部分です。玉虫色にせず、条件を示して言い切ります。

許可リスト方式を必ず実装すべきアプリケーションの三つの判断条件

次の三つのうち一つでも該当するなら、許可リスト方式の実装は省略できません。工数を理由に後回しにしないでください。

第一に、クラウド上で動き、インスタンスやタスクにロールが割り当てられている場合。認証情報が到達可能な位置にあるため、SSRFの一発が権限奪取へ直結します。第二に、同一VPC内に管理画面や内部APIが存在し、それらが送信元IPだけで認証を済ませている場合。SSRFはその認証を素通りします。第三に、利用者が入力したURLをサーバーが取得して内容を返す機能がある場合で、これはBasic SSRFの条件そのものでした。

該当するなら、実装順序は「宛先の許可リスト → 解決後IPの検証 → リダイレクト追従の停止 → egress制御」の順で入れます。最初の二つだけでも、実務で見つかるSSRFの大半は止まりました。

SSRF対策を薄くしてよい場面と、そのとき残す代替の防御線二つ

逆に、アプリケーション側の実装を薄くしてよい場面もあります。次のいずれにも当てはまるなら、コード側は簡易な検証にとどめて構いません。

  • 外向き通信が既定で遮断され、通信先が数個のホストに限定された環境で動いている
  • ロールを持たず、認証情報はすべて外部の秘密管理サービスから短命に取得している
  • 同一ネットワーク内に、送信元IPだけで認証する内部サービスが存在しない

このとき残す代替の防御線は二つです。ひとつは出口制御の設定そのものをコードと同じ管理下に置き、変更時にレビューを通す運用。もうひとつはメタデータサービスをIMDSv2のみへ強制し、稼働中インスタンスも含めて設定を定期的に監査すること。ネットワーク構成が変わった瞬間に前提が崩れるため、「今は薄くてよい」という判断は必ず条件つきで文書に残してください。

SSRFとCSRF・オープンリダイレクトの違いを表で整理する

名前が似ているため混同されやすい三つを並べます。誰がリクエストを出すのかで見分けるのが早道です。

観点 SSRF CSRF オープンリダイレクト
リクエストの発行元 サーバー 被害者のブラウザ 被害者のブラウザ
狙う対象 内部資産と認証情報 利用者の権限での操作 外部サイトへの誘導
主な被害 認証情報の窃取 意図しない更新処理 フィッシングの踏み台
基本の防御 宛先の許可リスト トークン検証とSameSite 遷移先の許可リスト

実務で厄介なのは、オープンリダイレクトがSSRFの回避手段に使われる点でした。許可ドメイン側に残っていると、そこを経由して内部アドレスへ飛ばせます。二つはセットで潰すのが確実です。ブラウザ起点の攻撃についてはXSSとCSRFの違いを比較した記事で詳しく扱っています。

SSRFの検出でSASTとDASTと手動診断が担う守備範囲の違い

見つけ方の話に移ります。ツールごとに得意な範囲が違うため、組み合わせ方を決めておきます。

SASTが拾えるSSRFの範囲と、静的解析だけでは届かない部分

静的解析は、利用者入力からHTTPクライアントの呼び出しまでの経路(ソースからシンクへの流れ)を追う形で検出します。単純な文字列連結でURLを組む実装は高い確率で拾えました。一方、設定ファイルやDBに宛先を持たせている実装、フレームワークの間接呼び出しを挟む実装では追跡が切れます。

加えてSASTは「検証関数を通っているか」までは見ても、その検証が正しいかを判断しません。許可リストのつもりでホスト名一致だけを見ている不備は、静的解析では合格になります。ツールの性質の違いはDASTとSASTの違いを整理した記事で確認できます。

Blind SSRFの確認に必要な帯域外コールバックの検証手順

動的診断でBlind SSRFを確かめるには、外部から観測できる受け口が要ります。手順はこうです。診断側で専用のドメインとサーバーを用意し、そのURLを入力として送り込む。対象アプリケーションのサーバーからDNS問い合わせやHTTPリクエストが届けば、応答が返らなくても「サーバーが接続を試みた」事実が確定します。

この帯域外(アウトオブバンド)の検証はCI上の自動テストにも組み込めます。受け口のアクセスログを結果として扱えば、防御が回帰的に効いているかを継続して確かめられました。

外部の脆弱性診断で確認したい観点と、社内テストとの分担の考え方

社内で回すのは回帰確認、外部に依頼するのは設計そのものの検査、という分担が現実的です。社内のテストは自分たちが想定した攻撃パターンしか通らないため、想定の外を埋める役割を外部の診断が担います。

依頼時に伝えたい観点は四つです。URLを受け取る機能の一覧、実行基盤とロールの割り当て状況、外向き通信の制御方針、リダイレクトとファイル変換の処理系。これがあると診断側はSSRFの入口を網羅的に洗えます。実装と構成をあわせて確認したい場合は、脆弱性診断・セキュリティ診断サービスで相談を受け付けています。

よくある質問

SSRFの実装相談で繰り返し受ける質問を、判断基準とあわせて整理しました。

SSRFとCSRFは何が違うのですか?

リクエストを出す主体が違います。CSRFは被害者のブラウザが、本人の権限でサーバーへリクエストを送らされる攻撃です。対してSSRFは、サーバー自身が攻撃者の指定した宛先へリクエストを送ってしまう攻撃になります。そのためCSRFの定石であるトークン検証やSameSite属性は、SSRFには一切効きません。防御策も監視すべき通信の向きも別物として扱ってください。

IMDSv2にすればSSRF対策は完了しますか?

完了しません。IMDSv2はクラウドのメタデータという「特定の宛先」を守る仕組みで、内部の管理画面や同一VPCのAPIへの到達は防げないためです。加えて、HTTPメソッドと任意ヘッダを操作できるタイプのSSRFではトークン取得そのものを実行される余地が残ります。IMDSv2は必須の設定ですが、アプリケーション側の許可リストとegress制御を別に用意する前提で考えてください。

危険なIPアドレスを拒否リストで弾く方式では不十分でしょうか?

不十分です。十進数表記、8進数表記、IPv4射影のIPv6アドレス、内部IPを返す公開DNS名、リダイレクト、DNSリバインディングと、拒否リストを回避する表記は次々に見つかっています。接続してよい宛先を列挙する許可リスト方式にしたうえで、名前解決した結果のIPがプライベート・ループバック・リンクローカル・予約済みに該当しないことを確認する二段構えにしてください。

Webhookのように任意URLの登録が仕様の場合はどうしますか?

許可リストが使えないため、守り方を変えます。第一に、送信専用のワーカーを内部ネットワークから隔離した場所に置き、そこから内部セグメントへ戻れない経路にすること。第二に、登録時点で解決したIPが内部レンジでないことを確認し、送信直前にも再確認すること。第三に、リダイレクトは追従しない設定にすること。この三つで、任意URL登録を仕様として残したまま被害範囲を封じられます。

SSRFはOWASP Top 10の何番目に分類されますか?

2021年版では A10:2021 Server-Side Request Forgery として単独項目でしたが、2025年版ではA01 Broken Access Control の収載CWE(CWE-918)として扱われています(2026年8月時点の公式掲載)。サーバーが本来アクセスを許されていない内部資源へ到達する認可の失敗、という位置づけへ整理された形です。項目番号が消えたことは軽視の意味ではなく、認可設計の問題として扱う判断だと読んでください。

関連記事

資料請求

RELATED POSTS 関連記事