【実務・中級編】サーバーサイドリクエストフォージェリ(SSRF)の攻撃シーケンスと防御 – アプリケーションセキュリティ & 安全な開発防御ガイド

SSRF:クラウド時代の「パンドラの箱」をどう封印するか

現場でインシデント対応をしていると、最近特に頭を抱えるのがSSRF(Server-Side Request Forgery)の巧妙化だ。昔は「外部から他人のサイトを叩かせる」程度の攻撃と思われていたが、現代のクラウド環境においてSSRFは、「クラウドインフラの心臓部(メタデータサービス)を握る鍵」に他ならない。

今日は、なぜSSRFがこれほどまでに危険なのか、そして我々エンジニアが「絶対に突破されない」ために何を実装すべきか、現場の視点で語ろうと思う。

1. なぜSSRFがクラウド環境で「終わりの始まり」なのか

AWSの`169.254.169.254`やGCPの`metadata.google.internal`といったメタデータサービスは、サーバー自身がIAM権限を取得するためのものだ。

もし、あなたが開発したアプリにSSRFの脆弱性があると、攻撃者は以下のようなステップで環境を掌握する。

1. 偵察: アプリのURL入力欄にメタデータサービスのIPを入力する。
2. 権限奪取: メタデータサービスから一時的な認証情報を取得する(`GET /latest/meta-data/iam/security-credentials/{role-name}`)。
3. 環境崩壊: 盗んだ認証情報でS3バケットを全公開したり、データベースをダンプしたりする。

これが「SSRF=ただのプロキシ」という認識が甘い理由だ。これは認証突破の踏み台である。

2. 脆弱な実装と攻撃の仕組み(PoCの視点)

まず、やってはいけない「脆弱な実装」の典型例(PHP)を見てほしい。

// 非常に危険!ユーザー入力をそのまま渡してはいけない
$url = $_GET[‘url’];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
echo $result;

攻撃者はここに `http://169.254.169.254/latest/meta-data/iam/security-credentials/my-role` と入力する。バリデーションがないため、サーバーは素直にメタデータを返却してしまう。これが「コピペで脆弱性を作る」コードの正体だ。

3. 防御の鉄則:許可リスト(Allowlist)の徹底

「URLの先頭が `https://api.example.com` で始まればOK」といった、安易な文字列比較はバイパスされる(`https://api.example.com.attacker.com` など)。

真の防御は、「IPアドレスの解決と検証」にある。以下にPythonでのセキュアな実装例を示す。

セキュアな実装(Python / Requests使用)

import requests
import socket
from urllib.parse import urlparse

def get_safe_content(url):
parsed_url = urlparse(url)
hostname = parsed_url.hostname

# 1. 許可リストに存在するホストか確認
allowed_hosts = [‘trusted-api.com’, ‘images.example.com’]
if hostname not in allowed_hosts:
raise ValueError(“不許可のホストです”)

# 2. IPアドレスを解決し、プライベート/ローカルIPでないか検証
ip = socket.gethostbyname(hostname)
if ip.startswith((‘127.’, ‘169.254.’, ‘192.168.’, ’10.’)):
raise ValueError(“内部ネットワークへのアクセスは禁止されています”)

# 3. 通信実行
response = requests.get(url, timeout=3)
return response.text

4. インフラレベルでの多層防御(WAF / IAM)

アプリのコードだけで守りきろうとするのは傲慢だ。インフラ側でも以下の設定を徹底してほしい。

IAMロールの引き締め

EC2やコンテナに付与するIAMロールには、「メタデータサービス以外へのアクセスを制限する」という概念はないが、「最小権限の原則」を適用せよ。もしS3へのアクセスが不要なら、その権限は絶対に付与しない。

AWS IMDSv2(必須設定)

AWSを利用しているなら、必ずIMDSv2を有効にしてほしい。トークンベースの認証が必要になるため、単純なSSRFではメタデータが取得できなくなる。

AWS CLIでメタデータ保護を強制する例
aws ec2 modify-instance-metadata-options \
–instance-id i-1234567890abcdef0 \
–http-tokens required \
–http-put-response-hop-limit 1 \
–http-endpoint enabled

  • `http-tokens required`: トークンなしのアクセスを拒否。
  • `http-put-response-hop-limit 1`: 外部からのプロキシ経由のアクセスを物理的に遮断する。

最後に:エンジニアとしてのマインドセット

SSRFを防ぐ最大の秘訣は、「自分のサーバーが、自分自身を信頼しすぎないこと」だ。

「内部ネットワークなら安全だ」という性善説を捨て、常に「リクエストの宛先は本当に外部の信頼できるサーバーか?」を自問自答してほしい。コードレビューの際、`curl`や`requests`、`fetch`といった外部通信を行う関数を見つけたら、そこに「ユーザー入力を検証するロジック」がセットになっているか、必ず確認するように。

セキュリティは一度の実装で終わりではない。システムが拡張されるたびに、新たな「盲点」が生まれる。その盲点を見つけるのが、我々エンジニアの腕の見せ所だ。

安全なコードを、明日も書いていこう。

コメント

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