こんにちは。現場で泥臭いインシデント対応を繰り返していると、「完璧な防御」なんて存在しないことに気づかされます。でも、だからこそ「どこを守れば被害を最小限にできるか」という勘所が大切になるんです。
今日は、最近多くのエンジニアを悩ませているSSRF(サーバーサイドリクエストフォージェリ)という攻撃について、一緒に学んでいきましょう。難しいセキュリティ用語が並んでいても大丈夫。身近な例えで、仕組みを噛み砕いていきますね。
—
1. SSRFって、結局なに?(泥棒の「代理人」作戦)
SSRFを直訳すると「サーバー側にリクエストを偽造させる」という攻撃です。これだけだとピンと来ませんよね。
家の中にある「留守番をしているAIスピーカー」を想像してみてください。そのAIスピーカーは、持ち主の命令があれば、どんなウェブサイトでも見に行ける機能を持っています。
もし、悪意のある泥棒が「あなたの家の鍵を開ける命令」を、このAIスピーカーにこっそり送ったらどうなるでしょう?
1. 泥棒は直接あなたの家には入れない(外の防壁があるから)。
2. でも、AIスピーカーは「持ち主からの命令だ!」と勘違いして、家の内側から鍵を開ける操作をしてしまう。
これがSSRFです。あなたのサーバーを「外部からの攻撃の踏み台(代理人)」として使い、本来なら外から見えないはずの「内部ネットワーク」へ侵入させるのがこの攻撃の正体です。
—
2. なぜこれが危険なの?
多くの開発現場では、「インターネット側(外側)は怖いから防御するけど、社内ネットワーク(内側)は安全だよね」という油断があります。
SSRFを許すと、攻撃者はこの「内側の安全神話」を悪用します。
- クラウドの秘密設定を盗む: AWSやGCPなどのメタデータサービス(IP: `169.254.169.254`)にアクセスさせ、一時的な認証キーを盗み出す。
- 社内限定の管理画面を叩く: 社内からしかアクセスできない設定変更画面を、あなたのサーバーを経由して操作する。
—
3. 防御の鉄則:許可リスト(Allowlist)で門番を立てる
一番やってはいけないのは、「ユーザーが入力したURLをそのまま信じてアクセスすること」です。
「このURLにアクセスしてね」と渡されたときに、「あらかじめ許可したリスト(許可リスト)」に載っているURLかどうかをチェックする「門番」を立てましょう。
実装のイメージ(Pythonでの例)
import urllib.parse
許可されたドメインのみをリスト化する
ALLOWED_DOMAINS = [“api.example-service.com”, “images.trusted-site.jp”]
def safe_request(user_input_url):
# 1. URLを解析する
parsed_url = urllib.parse.urlparse(user_input_url)
# 2. ドメインが許可リストにあるかチェック
if parsed_url.hostname not in ALLOWED_DOMAINS:
raise ValueError(“このURLへのアクセスは許可されていません!”)
# 3. 問題なければリクエストを実行
# return requests.get(user_input_url)
print(f”{parsed_url.hostname} へのアクセスを許可しました”)
攻撃者が入力しそうな例
safe_request(“http://169.254.169.254/latest/meta-data/”)
-> 門番が「許可されてません!」と止めてくれるので安心!
ポイントは「ブラックリスト(禁止リスト)ではなく、許可リストを使うこと」です。攻撃者はあの手この手で「禁止」の網をくぐり抜けてくるので、リストは「これだけはOK」という最小限のものにするのが鉄則です。
—
4. クラウドの「メタデータサービス」は物理的に遮断する
先ほど例に挙げた、クラウドの認証情報が詰まった `169.254.169.254` へのアクセス。これは、そもそもアプリケーションが絶対にアクセスする必要がない場所です。
これには「メタデータサービスへの通信をそもそも物理的に遮断する」というネットワークレイヤーでの防御が有効です。
- Linuxの場合: `iptables` などのファイアウォール設定で、`169.254.169.254` へのアウトバウンド通信をDROP(破棄)する設定を入れておきましょう。
サーバーからメタデータサービスへの通信を遮断する設定例
iptables -A OUTPUT -d 169.254.169.254 -j DROP
こうしておけば、仮にプログラムコードに脆弱性があったとしても、ネットワークの門番が「その先には行かせないよ」と遮断してくれます。二重の守り(多層防御)ですね。
—
最後に:完璧を目指さず、まずは「門番」を育てることから
セキュリティ対策は、一度やって終わりではありません。
「許可リスト」も定期的に見直す必要がありますし、新しい攻撃手法も日々生まれています。でも、まずは今日紹介した「ユーザー入力を信じない」「許可リストで縛る」「不要な場所への通信は捨てる」という3点を意識するだけで、あなたの開発するアプリケーションの堅牢性はぐっと向上します。
「面倒だな」と思わず、「自分の書いたコードが、誰かの大切なデータを守る盾になる」と思って、一歩ずつ取り組んでみてください。何かあったらまたいつでも聞きに来てくださいね。現場のエンジニアとして、いつでも応援していますよ!

コメント