悪魔の入り口「SSRF」を叩く:クラウドメタデータという名のパンドラの箱
現場でインシデント対応をしていると、往々にして「なぜこんな単純なミスで?」という箇所で大事故が起きている。その筆頭が SSRF (Server-Side Request Forgery) だ。
単なる「外部サイトへのリクエスト転送機能」だと思って実装したコードが、実はクラウド環境においては「そのサーバーの権限(IAMロール)を奪い、環境全体を掌握するための鍵」になることを認識していないエンジニアがあまりにも多い。今日は、SSRFがなぜ危険なのか、そしてどう防御すべきか、綺麗事抜きの「現場の作法」を伝授する。
—
1. 攻撃者がメタデータサービスを狙う理由
AWSの 169.254.169.254 や GCPの 169.254.169.254(metadata.google.internal)といったメタデータサービスは、そのインスタンスに紐付いたIAMロールの一時認証情報(AccessKey, SecretKey, Token)を平気で返してくる。
もし君のWebアプリが、ユーザーからの入力をそのまま curl や file_get_contents に渡しているとしたら、攻撃者は次のようなリクエストを投げるだけで、インフラの鍵を盗み出せる。
GET /proxy?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/your-role-name
これだけで、攻撃者の手元に一時的なクレデンシャルが渡る。あとは aws configure でそのキーを設定すれば、攻撃者は君のクラウド環境の中で「正規の管理者」として暴れまわることができる。これは脆弱性というより、事実上の「バックドア」だ。
—
2. 「許可リスト」によるバリデーションの限界と突破口
多くのエンジニアは「URLを正規表現でチェックすればいい」と考えがちだ。だが、甘い。以下のコードを見てくれ。
// 悪い例:正規表現による不完全なバリデーション
$url = $_GET['url'];
if (preg_match('/^https?:\/\/example\.com/', $url)) {
echo file_get_contents($url);
}
攻撃者は https://example.com@169.254.169.254/ といったURLや、DNSリバインディング、あるいはURLエンコードを駆使してこのチェックをすり抜ける。さらに、IPアドレスの直接指定や独自スキーム(gopher:// など)を悪用されると、バリデーションは無力化される。
—
3. 実践的防御策:セキュアな実装パターン
SSRFを防ぐための鉄則は「外部リクエストを直接受け付けない」ことだが、どうしても必要な場合は以下の3つの層で防御を固めろ。
① インフラ層:IMDSv2の強制
AWSであれば、必ず IMDSv2 を有効化し、セッション指向の認証を必須にしろ。これだけで、単なるGETリクエストによるクレデンシャル盗取はほぼ不可能になる。
- AWS CLIでの強制設定:
# IMDSv2を必須化(PUTリクエストによるセッション開始が必要になる)
aws ec2 modify-instance-metadata-options --instance-id i-xxxxxx --http-tokens required --http-endpoint enabled
② アプリ層:許可リストと名前解決の抑制
URLをパースしてIPを抽出し、プライベートネットワーク範囲(10.0.0.0/8, 169.254.169.254 等)を明示的に弾く。
import requests
from urllib.parse import urlparse
import ipaddress
import socket
def safe_get(url):
parsed = urlparse(url)
hostname = parsed.hostname
# 1. ホスト名のIPアドレスを解決
ip = socket.gethostbyname(hostname)
# 2. プライベートIPアドレスかチェック
if ipaddress.ip_address(ip).is_private:
raise Exception("不正なアクセス先です")
# 3. 許可されたドメインかチェック
if hostname not in ["api.trusted.com"]:
raise Exception("許可されていないドメインです")
return requests.get(url, timeout=3)
③ ネットワーク層:Egressフィルター
Webサーバーが直接インターネットのメタデータサービスへ通信することを、セキュリティグループやiptablesで遮断する。
- iptablesによるメタデータアクセス禁止設定:
# Webアプリが実行されているユーザー以外からのアクセスを拒否しつつ、メタデータへは絶対に行かせない
iptables -A OUTPUT -d 169.254.169.254 -j DROP
—
4. 最後に:エンジニアへの提言
SSRFを撲滅するために最も重要なのは、コードの書き方よりも「クラウドのメタデータサービスは、攻撃者にとっての『宝の山』である」という認識の共有だ。
実装時には必ず以下の問いを自分に投げかけてほしい。
- 「このURLはユーザーが直接操作できる必要が本当にあるか?」
- 「もしこのサーバーが乗っ取られたら、何ができるか?」
システム運用において、セキュリティは「機能」ではなく「品質」だ。動くものを作るのはスタート地点に過ぎない。君たちが書いたそのコードが、翌朝のニュースの種にならないよう、今日学んだ防御策をすぐにデプロイしてくれ。
もし実装で詰まったら、いつでも聞け。現場からは以上だ。
コメント