はじめに
Webアプリケーションの脆弱性のひとつに SSRF(Server-Side Request Forgery) があります。
「URLを受け取ってサーバーがリクエストする処理」で起きる脆弱性として知られていますが、実際にはURLを直接受け取るパラメータ以外にも、さまざまな機能が入口になります。
この記事では、SSRFの定義を一言で整理したうえで、SSRFが起こりうる代表的な入口(機能のパターン)を具体例とあわせて紹介します。
対象読者:
- SSRFという名前は知っているが、どこで起きるのかイメージが湧かない方
- 自分のサービスにSSRFの入口がないか確認したい方
SSRFとは
一言でまとめると、次のとおりです。
SSRFとは、ユーザーが影響を与えられる値をもとにサーバーが外部へリクエストする処理で、宛先を検証しないために、サーバーを踏み台にして内部ネットワークなど本来届かない場所へアクセスされる脆弱性。
さらに短くすると「サーバーに宛先を指定させて、内部へのリクエストを代行させる攻撃」です。
ポイントは、サーバーの ネットワーク上の位置 を悪用される点です。外部からは直接届かない次のような宛先に、サーバー経由で到達されてしまいます。
-
localhost上の管理画面や内部API - 社内ネットワーク上のサーバー
- クラウドのメタデータサービス(
http://169.254.169.254/など。認証情報を取得できる場合がある)
被害はデータの読み取りだけではありません。
- 内部の管理APIを叩かれ、設定変更や削除などの 操作 をされる
- 内部ネットワークのポートスキャンに使われる
- IPアドレスベースのアクセス制限を回避される
また、レスポンスが攻撃者に返らない Blind SSRF もあります。結果が見えなくても、内部へのリクエスト自体は届くため危険です。
もっとも基本的な形は、次のようなコードです。
// ユーザーから受け取ったURLを、検証せずにそのままサーバーから取得している
app.get("/fetch", async (req, res) => {
const response = await fetch(req.query.url);
res.send(await response.text());
});
/fetch?url=http://localhost:8080/admin のように指定されると、サーバーが内部の管理画面を取得し、その内容をユーザーに返してしまいます。
SSRFの主な入口
ここからは、SSRFが起こりうる代表的な機能を紹介します。共通点は「サーバーが、ユーザー由来の値をもとに外へ通信する」ことです。
画像URL取り込み
プロフィール画像やアイキャッチ画像を「URLから設定」できる機能です。
- 例:アイコン設定画面に画像URLを入力すると、サーバーが画像をダウンロードして保存する
- 内部URLを指定されると、サーバーがその宛先へリクエストを送る
- 画像として表示されなくても、エラーメッセージの内容や応答時間の違いから、内部の情報(ホストが存在するか、ポートが開いているか)が推測されることがある
Webhook設定
「イベント発生時にこのURLへ通知する」という機能です。
- 例:決済完了・ビルド完了・フォーム送信時に、指定したURLへPOSTリクエストを送る
- 通知先を内部APIに設定されると、サーバーが内部へPOSTを送り続ける
- レスポンスが見えない Blind SSRF になりやすい一方、内部の操作系API(削除・再起動など)を叩かれるリスクがある
- 「テスト送信」ボタンがあると、任意のタイミングで何度でも実行できてしまう
URLプレビュー(リンクカード)
チャットやSNSでURLを貼ると、タイトルやサムネイルを自動表示する機能です。
- 例:投稿にURLを含めると、サーバーがページを取得してOGP情報(タイトル・説明・画像)を抜き出す
- 内部URLを貼られると、取得したタイトルや本文の一部がカードとして表示され、内容が漏れる
- ユーザーが「URLを指定している」という意識が薄い機能のため、見落とされやすい
PDF・画像生成(HTMLレンダリング)
ユーザー入力を含むHTMLを、サーバー側でPDFや画像に変換する機能です。
- 例:請求書・レポート・証明書などをPDFで出力する機能(ヘッドレスブラウザやwkhtmltopdfなどを利用)
- 入力欄に次のようなタグを埋め込まれると、レンダラーがサーバー上で内部URLを読み込み、その内容がPDFに描画される
<iframe src="http://localhost:8080/admin"></iframe>
-
file:///スキームでサーバー上のローカルファイルを読まれるケースもある - URLを受け取っていないように見えても、HTMLを描画する時点でSSRFの入口になる
XML外部エンティティ(XXE)
XMLを受け付けるAPIやインポート機能です。
- 例:SOAP API、設定ファイルのインポート、SAML認証
- XMLには「外部エンティティ」という、外部のリソースを参照する仕組みがある
- パーサーが外部エンティティを解決する設定になっていると、XML内で指定された内部URLをサーバーが取得してしまう
<?xml version="1.0"?>
<!DOCTYPE data [
<!ENTITY ext SYSTEM "http://localhost:8080/internal">
]>
<data>&ext;</data>
- 多くのXMLパーサーでは外部エンティティ(DTD)を無効化する設定が用意されている
ファイル・ドキュメント処理
アップロードされたファイルを、サーバー側で変換・解析する機能です。
- SVG:中身はXMLなので、外部リソースへの参照を含められる。画像アップロードでSVGを許可している場合は注意
- Officeファイル(docx / xlsx など):中身はXMLの集まりで、外部リソースへの参照を持てる。サムネイル生成やテキスト抽出の際に参照が解決されることがある
- 画像・動画処理ライブラリ:ImageMagickやffmpegなどで、特定のファイル形式を処理する際に外部URLを読みに行く挙動が過去に問題になった(例:ffmpegのHLSプレイリスト)
「ファイルをアップロードしただけ」でも、処理の過程でサーバーが外部へ通信する可能性があります。
URLからのインポート機能
外部のデータを取り込む機能です。
- 例:「URLからCSVを取り込む」「Gitリポジトリをインポート」「RSSフィードを登録」
- 内部のファイルサーバーや管理画面のURLを指定される
- RSSのように定期取得する機能では、一度登録されると繰り返し内部へアクセスされる
認証連携の設定値
外部の認証サービスと連携するための設定項目です。
- 例:OAuth / OpenID Connect の設定で、IdPのURL(discovery URL、
jwks_uri)を入力できる画面 - 例:SAMLのメタデータURLを入力できる画面
- サーバーはそのURLから設定情報や鍵を取得するため、内部URLを指定されるとSSRFになる
- 特に、テナントごとに認証連携を設定できるマルチテナントSaaSで問題になりやすい
プロキシ・リダイレクト系パラメータ
外部リソースを中継するためのエンドポイントです。
- 例:
/proxy?url=...、/fetch?src=...、画像のリサイズ用/image?src=... - 冒頭のコード例と同じく、もっとも直接的な入口
- オープンリダイレクトとの組み合わせにも注意が必要。宛先を「自社ドメインのみ」に制限していても、自社ドメイン上にリダイレクト機能があると、そこを経由して内部へ飛ばされる
HTTPヘッダー
リクエストのヘッダーをもとに、サーバーが宛先を組み立てている場合です。
- 例:
HostやX-Forwarded-Hostヘッダーの値を使って、内部向けのURLを生成している - ヘッダーを書き換えられると、サーバーのリクエスト先が変わる
- URLパラメータではないため、入力値として意識されにくい
おわりに
SSRFの入口は「URLを入力する欄」だけではありません。HTMLの描画、XMLの解析、ファイルの変換、ヘッダーの利用など、サーバーがユーザー由来の値をもとに外へ通信する箇所はすべて入口になりえます。
自分のサービスを確認するときは、次の問いを機能ごとに当てはめてみるのがおすすめです。
このサーバーは、ユーザーが影響を与えられる値を使って外へ通信するか?
対策としては、主に次のようなものがあります。
- 宛先は許可リスト方式で制限する
- DNS解決後のIPアドレスで、プライベート・ループバック・リンクローカルのアドレスを拒否する(DNSリバインディング対策)
- リダイレクトを追従しない、または追従先も再検証する
- XMLパーサーで外部エンティティを無効化する
- クラウドではIMDSv2など、メタデータサービスへのアクセスを強化する
参考
- OWASP Top 10 2021 A10: Server-Side Request Forgery (SSRF)
- OWASP Server-Side Request Forgery Prevention Cheat Sheet
- PortSwigger Web Security Academy: Server-side request forgery (SSRF)
- PortSwigger Web Security Academy: XML external entity (XXE) injection
JISOUのメンバー募集中!
プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか?
興味のある方は、ぜひホームページをのぞいてみてください!
▼▼▼
https://projisou.jp
