SSRFという「境界線」の崩壊 —— ネットワーク分離とゼロトラストの深淵
多くのエンジニアが「SSRF(Server-Side Request Forgery)はURLをバリデーションすれば防げる」と信じ込んでいる。だが、現場で数多のインシデントを見てきた私から言わせれば、それは脆弱性の入り口に立ったに過ぎない。
SSRFの本質は、アプリケーションが「信頼された境界線」の内側にいるという慢心を逆手に取り、ネットワークプロトコルの仕様の隙間を突く「プロキシ攻撃」にある。今日は、教科書的な防御策のさらに奥、低レイヤのパケット構造やクラウドメタデータサービス(IMDS)の挙動という、エンジニアが最も見落としがちな防衛の要諦について話そう。
—
1. プロトコル仕様の落とし穴:リダイレクトとパケットの「変奏」
SSRFの防御で最も失敗しやすいのが、HTTPクライアントライブラリのデフォルト挙動だ。多くのライブラリは、攻撃者が制御するURLへのリクエストに対し、透過的にリダイレクト(301/302/307/308)を追従する。
もし、貴方のアプリケーションが `http://trusted-internal-service/` を叩こうとして、攻撃者が `http://attacker-controlled-site/` を経由して内部の `http://169.254.169.254/latest/meta-data/` にリダイレクトさせた場合、そのバリデーションは意味をなさない。
防御の要:HTTPクライアントのハードニング
URLの文字列チェックではなく、「通信のライフサイクル」を制御せよ。以下は、Go言語における堅牢なHTTPリクエストの定石である。
// 安全なHTTPクライアントの設計
func CreateSecureClient() http.Client {
return &http.Client{
// 1. リダイレクトを無効化、あるいは厳格に制御する
CheckRedirect: func(req http.Request, via []http.Request) error {
return errors.New(“リダイレクトは許可されていません”)
},
Transport: &http.Transport{
// 2. ダイヤラーを差し替え、内部IPへの接続を遮断する
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
host, _, _ := net.SplitHostPort(addr)
ip := net.ParseIP(host)
// プライベートIP範囲(RFC 1918)へのアクセスをハードコードで拒絶
if ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalMulticast() {
return nil, fmt.Errorf(“許可されていないネットワークへのアクセス: %s”, host)
}
return (&net.Dialer{Timeout: 5 time.Second}).DialContext(ctx, network, addr)
},
},
}
}
—
2. IMDSの遮断:クラウドインフラの最後の砦
クラウド環境において、SSRFの最大の標的はEC2やGCEが提供する「メタデータサービス(IMDS)」だ。ここにはIAMロールの認証情報が含まれており、一度突破されれば、そのノードの権限が剥奪される。
現代のアーキテクトであれば、以下のガードレールは必須だ。
- IMDSv2の強制: トークンベースのセッション認証(PUTリクエスト)を要求し、単純なGETリクエストによるアクセスを封じる。
- TTLの最小化: IMDSへのホップ数を制限し、コンテナ内からのアクセス経路を物理的に切断する。
- ネットワークポリシーによる分離: Kubernetes環境であれば、`NetworkPolicy`を用いて、特定のPodからIMDS(`169.254.169.254`)へのEgressを明示的に拒否せよ。
Kubernetes NetworkPolicyによるメタデータサービスへのアクセス遮断
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-metadata-access
spec:
podSelector: {} # 全Podを対象
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 169.254.169.254/32 # メタデータサービスへの通信をピンポイントで遮断
—
3. 生成AI時代の「インジェクション」とガードレイル
昨今、LLMを用いたアプリケーションにおいて、プロンプトインジェクションとSSRFが結合した攻撃が増加している。LLMが「外部URLの内容を取得して要約せよ」という命令を受けた場合、LLM自体がプロキシとして機能してしまうのだ。
この際の防御は、アプリケーション層のバリデーションでは不十分だ。「サンドボックス化されたプロキシアーキテクチャ」を導入せよ。
1. 分離された実行環境: URL取得用の処理は、メインのLLMアプリケーションとは完全に独立したVPC内のコンテナ(使い捨ての短命なコンテナ)で行う。
2. Egressフィルタリング: そのコンテナは、ホワイトリストに登録されたFQDN以外へのアウトバウンド通信を一切許可しない。
3. コンテンツ検査: 取得したペイロードは、LLMに渡す前に、PII(個人情報)や悪意のあるスクリプトが含まれていないか、別のスキャナ(ガードレールモデル)を通す。
—
最後に:防御は「疑うこと」から始まる
SSRFを防ぐということは、貴方のシステムが「何を信頼し、何を拒絶するか」という論理的な境界線を明文化することに他ならない。
メモリ上の低レイヤな挙動まで理解し、プロトコルの仕様をハックする攻撃者に対し、我々が勝つ方法はただ一つ。「防御の多重化」だ。コードレベルでのチェック、コンテナレベルでのネットワーク制御、そしてクラウドインフラレベルでの権限分離。これらを繋ぎ合わせ、一枚岩ではなく、強固な網目状の防壁を構築せよ。
セキュリティは静的な状態ではない。常に動く脅威に対し、アーキテクチャを研ぎ澄まし続けること。それが我々エンジニアの矜持だ。

コメント