SSRF:クラウド時代の「開かずの扉」をこじ開ける盲点
おい、エンジニアのみんな。今日は「SSRF(Server-Side Request Forgery)」について話そう。
OWASP Top 10の常連だが、教科書通りに「外部URLを叩ける機能が危ない」とだけ理解しているなら、君のシステムはとっくに食い物にされているかもしれない。特にクラウド環境において、SSRFは単なる「外部サイトへのアクセス」を超え、クラウドインフラの心臓部を奪うパスポートに化ける。
今日は、なぜSSRFが恐ろしいのか、そしてどうやって実務レベルで完封するのか、現場の泥臭い知見を共有する。
—
1. なぜSSRFが「クラウドの鍵」になるのか
攻撃者が狙うのは、君たちが一生懸命作ったWebアプリじゃない。その裏側にある「クラウドメタデータサービス(IMDS)」だ。
AWSであれば http://169.254.169.254/ がそれにあたる。ここには、インスタンスに付与されたIAMロールの認証情報(一時クレデンシャル)が平文で転がっている。
攻撃シーケンスのリアル
1. 探索: 攻撃者はアプリの「URLを指定して画像を取得する機能」などを使い、ローカルIP(169.254.169.254)へのリクエストを試みる。
2. 突破: アプリがサーバーサイドでそのURLをフェッチ(GET)してしまう。
3. 窃取: 攻撃者はIAMロールの認証情報を取得し、君のAWSアカウントの権限でS3バケットを覗き見たり、DBをダンプしたりする。
この時、防御側がやるべきは「URLのバリデーション」だが、正規表現でURLを弾こうとするのは初心者の罠だ。http://169.254.169.254/ を http://127.0.0.1 や「短縮URL」「DNSリバインディング」などで回避するのは、攻撃者にとって朝飯前だからな。
—
2. 完封するための防御戦略
SSRFを防ぐための鉄則はたった一つ。「信頼できない入力をそのままHTTPクライアントに渡さないこと」だ。
実装のポイント
- 許可リスト(Allowlist)の徹底: 許可されたドメイン名(例:
api.trusted-service.com)以外は全て拒否する。 - ネットワーク分離: Webアプリがメタデータサービスにアクセスできないよう、IAMロールの権限を最小化し、必要に応じてネットワーク境界で遮断する。
- IMDSv2の強制: AWSを使っているなら、セッション指向のIMDSv2を強制しろ。これだけで、単純なGETによる認証情報の窃取は防げる。
—
3. 実践:セキュアなURL検証とフェッチ(Python実装例)
以下は、requestsライブラリを使って安全に外部リソースを取得するための実装サンプルだ。単なる文字列チェックではなく、IP解決後のチェックを行っているのがポイントだ。
import requests
import socket
from urllib.parse import urlparse
許可されたドメインのホワイトリスト
ALLOWED_DOMAINS = [“api.trusted-service.com”, “images.example.com”]
def get_safe_resource(url):
parsed = urlparse(url)
# 1. ホスト名が許可リストにあるか確認
if parsed.hostname not in ALLOWED_DOMAINS:
raise ValueError(“不許可のドメインです”)
# 2. IPアドレスを解決して、プライベートIPでないことを確認
# (DNSリバインディング対策のため、解決したIPで最終判定する)
ip_address = socket.gethostbyname(parsed.hostname)
if ip_address.startswith((“169.254.”, “127.”, “10.”, “172.16.”, “192.168.”)):
raise ValueError(“プライベートネットワークへのアクセスは禁止されています”)
# 3. 安全なリクエスト実行
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
return response.content
except requests.exceptions.RequestException as e:
# ログには詳細を出すが、ユーザーには汎用的なエラーを返す
print(f”Error: {e}”)
return None
—
4. インフラ層でのダメ押し
コードだけで防ごうとするな。インフラ側にも「二重の鍵」をかけるのがプロの仕事だ。
AWSの場合:IMDSv2の強制
CloudFormationやTerraformで、インスタンス作成時に以下を設定しろ。
AWS CLIでのIMDSv2強制設定例
aws ec2 modify-instance-metadata-options \
–instance-id i-xxxxxx \
–http-tokens required \
–http-put-response-hop-limit 1 \
–http-endpoint enabled
http-tokens required: セッショントークンを必須にする。http-put-response-hop-limit 1: リクエストの転送を遮断する。
Nginxでの遮断(防御的設定)
どうしてもアプリ側で防ぎきれない場合の最終手段として、WAFでリクエストをフィルタリングするか、Nginx側で内部APIへのアクセスを禁止するルールを書いておけ。
—
最後に:セキュリティは「疑うこと」から始まる
いいか、エンジニア諸君。SSRFの修正は、コードを書き換えて終わりではない。
「自分たちが想定していないネットワーク経路」を常に想像することだ。
もし君のプロダクトでURL入力機能があるなら、今すぐ 169.254.169.254 を入力して試してみろ。もし認証情報が返ってきたら……それが君のシステムの「今の実力」だ。
修正は今すぐやれ。明日になれば、攻撃者が君のインフラを乗っ取っているかもしれないんだからな。
コメント