SSRFの恐怖:なぜ「URLのバリデーション」だけでは不十分なのか
現場でインシデント対応をしていると、開発者が「URLを正規表現でチェックしているから大丈夫」と胸を張る場面によく遭遇する。だが、残念ながらその認識こそが、攻撃者の格好の餌食になる。
SSRF(Server-Side Request Forgery)は、単なる「外部サイトへのアクセス」ではない。攻撃者は、あなたのサーバーを「信頼された内部ネットワークの踏み台」として利用する。AWSのメタデータサービス(IMDSv2以前)へのアクセスや、外部から遮断されているはずの管理画面へのログイン試行が、あなたのサーバーを経由して行われるのだ。
今日は、教科書的な「ドメイン許可リスト」のその先にある、泥臭くも確実な防御策を共有する。
—
1. 攻撃者が好む「盲点」:チェックをすり抜ける手口
攻撃者は、あなたが実装したバリデーションの「実装上の不一致」を突いてくる。
- DNS Rebinding: 最初のチェック時には安全なドメインを返し、リクエスト実行時には内部IPを返すようにDNSを操作する。
- IP表現の難読化: `127.0.0.1` を `0x7f000001` や `2130706433` といった10進数、あるいは省略記法 `127.1` に変換してフィルタを回避する。
- リダイレクトの悪用: 指定したURL自体は許可リストにあっても、そこから内部ネットワークへリダイレクト(302)させ、チェックをバイパスする。
これらに対処するには、「文字列のチェック」ではなく「解決後のIPアドレスの検証」が不可欠だ。
—
2. 鉄壁の防御:許可リストとIPフィルタリング(Python実装)
単純な文字列一致ではなく、ドメインを解決して得られたIPアドレスが「内部ネットワーク(RFC1918など)」に該当しないかを確認するのが、実務上の正攻法だ。
import socket
import ipaddress
import requests
from urllib.parse import urlparse
def is_safe_url(target_url):
# 1. 許可されたドメインのみを許可する(ホワイトリスト)
allowed_domains = [‘api.trusted-service.com’, ‘cdn.example.com’]
parsed = urlparse(target_url)
if parsed.hostname not in allowed_domains:
return False
# 2. DNS解決してIPを取得
try:
ip = socket.gethostbyname(parsed.hostname)
except socket.gaierror:
return False
# 3. 内部IPアドレス(プライベートIP)へのアクセスを遮断
# 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 をチェック
ip_obj = ipaddress.ip_address(ip)
if ip_obj.is_private or ip_obj.is_loopback or ip_obj.is_link_local:
return False
return True
実行例
url = “http://api.trusted-service.com/data”
if is_safe_url(url):
# リクエストを送信する際は、allow_redirects=False に設定してリダイレクトを制御する
response = requests.get(url, allow_redirects=False, timeout=5)
print(“安全なリクエストを実行しました”)
else:
print(“警告:不正なURLです”)
—
3. インフラ側での「最後の砦」
アプリケーションのコードだけで100%防ぐのは難しい。もしアプリが突破されたとしても、影響を最小限にするためにネットワーク層で蓋をする。
Nginxでのリバースプロキシ設定(出口制限)
アプリケーションサーバーから外部への通信を制限する場合、プロキシ側で宛先を絞るのが賢明だ。
信頼できない宛先へのリクエストをプロキシで遮断する設定例
location /fetch-url {
# アプリケーションサーバーからの直接的な外部通信を禁止し、
# 特定のプロキシを経由させることで、ここでフィルタリングをかける
proxy_pass http://external-gateway;
# 内部ネットワーク(10.0.0.0/8等)へのアクセスをここで拒否
deny 10.0.0.0/8;
deny 172.16.0.0/12;
deny 192.168.0.0/16;
}
AWS環境でのメタデータ保護(IMDSv2)
もしあなたがAWSを使っているなら、IMDSv2(Instance Metadata Service Version 2)を必須化するだけで、多くのSSRFによるクラウド資産奪取を防げる。
- 対策: インスタンスプロファイルに `HttpTokens=required` を設定する。これにより、セッション指向の認証が必須となり、単純なGETリクエストだけではメタデータが取得できなくなる。
—
最後に:セキュリティは「多層防御」の積み重ねだ
「許可リストを作ったから完璧だ」という思考停止が、最大のリスクだ。URLバリデーションはあくまで最初の防衛線。その先に、「リダイレクトの禁止」「IPの検証」「出口のネットワーク制限」「クラウドの権限最小化(IAMポリシーでのアクセス制限)」を組み合わせる。
泥臭いかもしれないが、この多層防御こそが、攻撃者に「このターゲットは面倒だ」と思わせ、次の標的へ向かわせる最強の盾になる。コードを書くとき、インフラを触るとき、常に「もしここを攻撃者に悪用されたら、何が見えてしまうか?」を想像してほしい。
それができるエンジニアこそが、真のセキュリティ・プロフェッショナルだ。

コメント