【実務・中級編】 サーバーサイドリクエストフォージェリ(SSRF)による内部ネットワーク探索 – オフェンシブセキュリティ & リバースエンジニアリング防御ガイド

境界線は幻想だ:SSRFで内部ネットワークを「食い尽くす」手口と防御の鉄則

「ファイアウォールがあるから安全」「VPC内だから外部からは見えない」――そう信じているなら、今すぐ考えを改めてほしい。外部から直接触れない内部APIや管理画面が、Webサーバーという「一番外側の門番」の裏切りによって、いとも簡単に引きずり出されるのがSSRF(Server-Side Request Forgery)の恐ろしさだ。

今日は、我々レッドチームがペネトレーションテストで「最初に狙う穴」の一つ、SSRFについて、現場の知見を交えて徹底的に解説する。

—

1. SSRFの正体:なぜ「サーバーの代筆」が脅威になるのか

SSRFの核心は、「サーバーに、本来アクセスすべきでない場所へリクエストを代行させること」にある。

攻撃者はWebサーバーの脆弱性を突き、自分たちの代わりに内部ネットワークの探索を行わせる。例えば、AWSのメタデータサービス(169.254.169.254)を叩かせてIAMロールの認証情報を奪ったり、内部のRedisやElasticsearchにコマンドを送り込んでデータを破壊・流出させるのが定石だ。

攻撃のシグナル

もしあなたのサーバーのアクセスログに、以下のような奇妙なリクエストが散見されるなら、既に探索が始まっているかもしれない。

  • ?url=http://localhost:8080/admin
  • ?image_url=http://169.254.169.254/latest/meta-data/
  • ?callback=http://internal-db.local:6379/

—

2. 現場で使える防御の鉄則:ホワイトリスト以外は「悪」と見なせ

SSRFを防ぐために「ブラックリスト方式(特定のIPを拒否する)」を採用するのは、水漏れをセロハンテープで塞ぐようなものだ。IPの難読化やDNSリバインディングを使えば、いとも簡単にすり抜けてくる。

唯一の正解は、「ホワイトリストによる厳格な許可」だ。

Pythonでの実装例:安全なリクエストの設計

外部からURLを受け取ってコンテンツを取得する際、ライブラリのデフォルトに任せてはいけない。ホスト名、ポート、プロトコルを厳格にバリデーションする実装が必要だ。

import requests
from urllib.parse import urlparse

# 許可するホストのホワイトリスト
ALLOWED_HOSTS = ['api.trusted-service.com', 'images.cdn.com']

def fetch_url(target_url):
    parsed_url = urlparse(target_url)
    
    # 1. プロトコルの制限 (http/httpsのみ許可)
    if parsed_url.scheme not in ['http', 'https']:
        raise ValueError("無効なプロトコルです")
        
    # 2. ホスト名のバリデーション
    if parsed_url.netloc not in ALLOWED_HOSTS:
        raise ValueError("許可されていないホストへのアクセスです")

    # 3. リクエスト実行(リダイレクトを追跡しない設定が重要)
    try:
        response = requests.get(target_url, timeout=2, allow_redirects=False)
        return response.content
    except requests.exceptions.RequestException as e:
        return f"エラーが発生しました: {e}"

—

3. インフラレベルでの「トドメ」を刺す設定

コード側の修正はもちろん必要だが、二重の防御としてインフラ設定も重要だ。

AWS環境:メタデータサービス(IMDSv2)の強制

AWSを使っているなら、メタデータサービスへのアクセスを強化する IMDSv2 を必須に設定しよう。これだけで、単純なSSRFによる認証情報の奪取を大幅に防げる。

# AWS CLIでメタデータサービスをIMDSv2専用にするコマンド
aws ec2 modify-instance-metadata-options \
    --instance-id i-xxxxxxxxxxxxxxxxx \
    --http-tokens required \
    --http-put-response-hop-limit 1

Nginx:内部ネットワークへのルーティング制限

Webサーバーが不用意に内部ネットワークへパケットを投げないよう、iptables や nftables でEGRESS(外向き)トラフィックを制限するのがプロのやり方だ。

# 内部ネットワーク(10.0.0.0/8)への通信をサーバー自体から遮断する例
iptables -A OUTPUT -d 10.0.0.0/8 -j DROP

—

4. エンジニアへのアドバイス:脆弱性は「設計」の段階で潰す

現場でインシデントを見てきた経験から言わせてもらうと、SSRFを許してしまうシステムは、大抵の場合「何でもURLを渡せば処理してくれる柔軟すぎる機能」が原因だ。

  • URLを直接ユーザーから受け取らない: 可能ならIDで管理し、サーバー側でURLを組み立てる設計に変える。
  • リダイレクトを無効にする: curl や requests のデフォルト設定は要注意だ。攻撃者はリダイレクトを悪用して内部IPに誘導する。
  • DNS解決を制御する: 外部から指定されたホスト名が内部IPを指している場合、解決の段階で弾くバリデーションを組み込む。

セキュリティは「どこか一つの設定を変えればOK」という魔法ではない。アプリケーションのコード、インフラの境界、そして運用監視。これらが噛み合って初めて、強固な城壁となる。

今日からあなたの書くコードが、次のインシデントを防ぐ防波堤になることを期待している。不明点があれば、またいつでも聞いてくれ。現場からは以上だ。

コメント

タイトルとURLをコピーしました