こんにちは!インフラやセキュリティの世界に一歩踏み出したばかりの皆さん、日々の開発や運用お疲れ様です。
新しいシステムを作ったり、便利な機能を外部のAPIと連携させたりする時ってワクワクしますよね。「お、自分の書いたコードが別のサーバーと会話している!」という感動は、エンジニアならではの醍醐味だと思います。
でも、その「サーバー同士の会話」をちょっとした油断から悪用されてしまうのが、今日お話しするSSRF(サーバーサイドリクエストフォージェリ)というサイバー攻撃です。
今回は、クラウドの裏側でこっそり起きる怖いお話と、それをピシャリと防ぐためのスマートな対策について、身近な防犯に例えて一緒に優しく学んでいきましょう!
—
1. 家の鍵に例えて理解する「SSRF」の正体
まずは、私たちの身近な「お家」を想像してみてください。
皆さんの家には頑丈な玄関の鍵がありますよね。泥棒が正面から入ってこられないように、鍵をしっかり閉めて警備会社とも契約しているはずです。これが通常のWebサイトのセキュリティです。
では、あなたの家に「外の便利なお店に、出前を頼んで持ってきてくれるお使いロボット」が住んでいるとしましょう。
このロボットは、あなた(主人)の命令を受けて、外の世界に自由に行き来して買い物をすることができます。
ここで、もし「悪意を持った泥棒」がこのロボットにこっそり近づいて、こんな風に耳元で囁いたとしたらどうでしょう?
> 「ねえ、近所のスーパーじゃなくて、この家の中にある『金庫の部屋』に行って、そこにある大事な合鍵を取ってきてよ!」
お使いロボットは素直なので、あなたの命令ではなくても、自分に与えられた権限を使って家の中の金庫の部屋に入り込み、大切な合鍵を取って泥棒に渡してしまいます……。
これが、SSRF(Server-Side Request Forgery:サーバーサイドリクエストフォージェリ)の仕組みです。
Webアプリケーション(お使いロボット)が、ユーザーから受け取ったURLやデータをろくに確認せず、そのまま内部のシステムやクラウドの管理画面(金庫の部屋)にアクセスしてしまうことで、本来見せてはいけない秘密の情報を盗み出されてしまう攻撃なのです。
—
2. 狙われるのはクラウドの「心臓部」(IMDSv2の脅威)
現代のWebシステムの多くは、AWS(Amazon Web Services)などのクラウド上で動いています。
クラウドの世界には、サーバー自身が「今、自分はどんな環境で動いているんだっけ?」を確認するための、いわば「サーバー専用の自己紹介カード置き場」のような特別な場所が存在します。これがIMDS(インスタンスメタデータサービス)と呼ばれるものです。
このメタデータサービスには、クラウドの管理権限を持つ強力なパスワード(認証トークン)などが保管されています。もし攻撃者がSSRFを使って、あなたのサーバーにこのメタデータサービスへアクセスさせることができたら……?
「お使いロボット」が、クラウドのマスターキーを勝手に拾って攻撃者に渡してしまうようなもので、システム全体が乗っ取られる大惨事につながってしまいます。
特に古いバージョンの仕組み(IMDSv1)では、ただ「この住所にアクセスして!」と言われるだけで簡単に中身を渡してしまいましたが、現在の最新バージョンであるIMDSv2では、いくつかの「合言葉」をやり取りしないと中身を教えない仕組みになり、安全性がぐっと高まっています。
—
3. 実装の現場で防ぐ!「許可リスト」と「ヘッダー」の力
では、新人の私たちが開発するシステムを、この卑劣なSSRFから守るにはどうすればよいのでしょうか?
ここからは、実際のプログラムや設定をイメージしながら、具体的な防衛策を見ていきましょう。
対策①:行先を厳しく制限する「許可リスト(ホワイトリスト)」
よくやってしまいがちな失敗が、「ユーザーが入力したURLの形が、なんとなく http:// から始まっていればOKにしよう」というガバガバな判定です。これでは http://169.254.169.254/(クラウドのメタデータ置き場)のような危険な住所もスルーされてしまいます。
安全な鉄則は、「あらかじめ安全だと分かっている行き先(ドメイン)のリスト」を作っておき、それ以外には一歩も出歩かせないことです。
PHPを例に、安全なURLだけを受け付けるコードの書き方を見てみましょう。
<?php
/**
* ユーザーから受け取ったURLに安全にアクセスするためのサンプル関数
* SSRF対策として「許可リスト」による厳格なドメインチェックを行います。
*/
function safeFetchUrl(string $userUrl): string {
// 1. まずはパース(分解)して、ドメイン(ホスト名)を取り出す
$parsedUrl = parse_url($userUrl);
$host = $parsedUrl['host'] ?? '';
// 2. あらかじめ安全と許可されたドメインのリスト(ホワイトリスト)
$allowedHosts = [
'api.weather-service.example.com',
'data.partner-site.example.org'
];
// 3. 許可リストの中に、ユーザーが指定したドメインが含まれているか厳しくチェック!
if (!in_array($host, $allowedHosts, true)) {
// リストにない場合は、即座に処理を中断してエラーにする
throw new \InvalidArgumentException('エラー: 許可されていない宛先へのアクセスです。');
}
// 4. チェックをクリアした安全なURLだけにリクエストを送信する
$ch = curl_init($userUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
?>
このように、「行く場所をあらかじめ決めておく」だけで、お使いロボットが変な場所に連れ出されるのをピタッと防ぐことができます。
—
対策②:クラウドの守りを固める「IMDSv2」とヘッダーの秘密
もし皆さんがAWSなどのクラウドインフラを構築・管理する立場になったら、クラウド側でもしっかりと防御の鍵をかけましょう。
先ほど登場したIMDSv2では、メタデータを取りに行く際に「セッションのトークン(一時的なパスコード)」を発行してもらい、それをリクエストのヘッダーに含める必要があります。
攻撃者が単純なSSRF攻撃でサーバーに命令文(HTTPリクエスト)を送ったとしても、この「特別なヘッダー」を勝手に偽装して付与することは難しいため、攻撃を大きく防ぐ盾になります。
インフラの設定(AWSのTerraformやCloudFormation、またはAWS CLIなど)を行う際は、古い「v1」を無効化し、必ず「v2」を必須にする設定を有効化しましょう。
// AWSのインスタンスメタデータ設定のイメージ(IMDSv2を強制する設定)
{
"InstanceMetadataOptions": {
"HttpTokens": "required", // 「v2」のトークン利用を必須にする(これが最強の防衛!)
"HttpEndpoint": "enabled" // メタデータサービス自体は有効にしておく
}
}
この HttpTokens を required(必須)に設定することで、お使いロボットは「ちゃんと合言葉(トークン)を持ってきた人だけにお知らせを渡す」ようになり、セキュリティが劇的に向上します。
—
まとめ:一歩ずつ、安全なコードとインフラを目指して
今回は、サーバーサイドリクエストフォージェリ(SSRF)という少し難しそうな攻撃のメカニズムと、その対策についてお話ししました。
- SSRFとは: サーバーの「お使い機能」が悪用され、内部の秘密情報やクラウドの心臓部を覗き見られてしまう攻撃。
- アプリケーションの対策: ユーザーの言うことを何でも聞くのではなく、「許可リスト(ホワイトリスト)」を使って行先を厳しく制限する。
- インフラの対策: クラウドのメタデータサービスには「IMDSv2(トークン必須)」を設定し、不正なアクセスを寄せ付けない。
セキュリティの世界は覚えることがたくさんあって最初は圧倒されてしまうかもしれませんが、一つひとつの仕組みを「身近な防犯」に置き換えて考えていくと、とてもスッキリ理解できるようになります。
「自分の書いたコードは本当に安全な宛先を見に行っているかな?」
そんな視点をちょっとだけ持つだけで、あなたも立派なセキュア・コーダーの仲間入りです。
一歩ずつ、安心して任せられるエンジニアを目指して一緒に頑張っていきましょう!
コメント