【実務・中級編】 IMDSv2(Instance Metadata Service Version 2)の強制とSSRF対策 – インフラ・ネットワーク & クラウドセキュリティ防御ガイド

クラウドの「鍵」を盗まれる前に。IMDSv2強制によるSSRF完全防御の極意

エンジニア諸君、お疲れ様。今日もどこかのサーバーで脆弱性が突かれ、インシデント対応に追われている現場があることだろう。

今回は、AWS等のクラウド環境における「SSRF(Server-Side Request Forgery)」の防波堤として、今や必須事項であるIMDSv2(Instance Metadata Service Version 2)の強制について語る。

「まだIMDSv1を使っている?」「http://169.254.169.254 へのアクセスなんて、うちのアプリはしないから大丈夫?」……その甘い認識が、企業の重要資産を路頭に迷わせる。この設定は、単なる「推奨」ではなく「生存戦略」だ。

—

1. なぜIMDSv1は「終わった」のか?

IMDSv1は、認証なしでリクエストを投げるだけでインスタンスのメタデータを取得できる仕組みだった。攻撃者は、Webアプリに存在するSSRFの脆弱性を突き、このエンドポイントを叩く。

攻撃シナリオ(PoC)

例えば、ユーザーがURLを入力するとその内容をサーバー側で取得して表示するような機能があるとしよう。攻撃者は以下のようにリクエストを操作する。

GET /fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/[ロール名]

これだけで、IAMロールの認証情報(AccessKey/SecretKey/Token)が平文で返ってくる。これを入手した攻撃者は、君たちのAWSアカウントの「中身」を好き勝手に操作できる状態になる。これが「メタデータ盗難」の恐ろしさだ。

IMDSv2では、これに「セッショントークン」という物理的な壁を設けることで、SSRFによる単発のGETリクエストではメタデータを取得できないように設計されている。

—

2. IMDSv2への移行と強制手順

まずは、IMDSv2を強制するための設定を確認しよう。TerraformやCloudFormationを使っているなら、今すぐ以下の設定を確認してほしい。

AWS CLIによる強制設定

既存インスタンスに対して、IMDSv2を必須化するコマンドだ。

# IMDSv2を必須化し、v1を無効化する
aws ec2 modify-instance-metadata-options \
    --instance-id i-xxxxxxxxxxxxxxxxx \
    --http-tokens required \
    --http-put-response-hop-limit 1 \
    --http-endpoint enabled
  • http-tokens required: これが肝だ。セッショントークン必須を強制する。
  • http-put-response-hop-limit 1: これを 1 にすることで、コンテナ内からのアクセスや、多段プロキシを経由したアクセスを遮断し、SSRFの影響範囲を最小化する。

—

3. アプリケーションコードでのセキュアな実装

「じゃあ、プログラムからメタデータを取得したいときはどうすればいいのか?」という疑問に対して、正しい実装サンプルを提示する。Python(boto3)や標準的なHTTPクライアントを使う場合だ。

Pythonでの実装例

boto3 を使うのがベストだが、もし低レイヤーでHTTPリクエストを叩く必要がある場合のロジックがこれだ。

import requests

def get_metadata_token():
    """IMDSv2用のセッショントークンを取得する"""
    headers = {'X-aws-ec2-metadata-token-ttl-seconds': '21600'}
    # PUTメソッドでトークンを要求
    response = requests.put(
        'http://169.254.169.254/latest/api/token', 
        headers=headers
    )
    return response.text

def get_metadata(path):
    """トークンをヘッダーに付与してメタデータを取得"""
    token = get_metadata_token()
    headers = {'X-aws-ec2-metadata-token': token}
    response = requests.get(
        f'http://169.254.169.254/latest/{path}', 
        headers=headers
    )
    return response.text

# 利用例: インスタンスIDを取得
print(get_metadata('meta-data/instance-id'))

見ての通り、ステップが2つに増えている。単純なSSRFの脆弱性だけでは「トークン取得(PUT)」と「メタデータ取得(GET)」の両方を完遂することは非常に困難だ。

—

4. 現場のプロとしての提言:防御の多層化

IMDSv2の強制は「最低限の防御」だ。さらに堅牢なインフラにするために、以下の対策を忘れないでほしい。

1. IAMロールの最小権限化: 万が一メタデータを盗まれても、被害を最小限にするために、インスタンスに付与するIAMロールには「必要な権限のみ」を記述すること。
2. ネットワーク分離: メタデータエンドポイントへのアクセスを、アプリケーションが必要とする通信以外制限できないか検討する。
3. WAFによる防御: Webアプリの入り口で、URLパラメータに 169.254.169.254 や metadata という文字列が含まれるリクエストをブロックするWAFルールを設定しておく。これは「多層防御」のセオリーだ。

NginxでのSSRFブロック例(設定ファイル)

もし、どうしてもWebアプリでSSRFの懸念が拭えない場合、リバースプロキシで止めるのも手だ。

# nginx.conf 内のサーバーブロック
location / {
    # メタデータIPへのリクエストを拒否
    if ($arg_url ~* "169.254.169.254") {
        return 403;
    }
    proxy_pass http://backend_app;
}

—

最後に

セキュリティとは、完璧な製品を導入することではない。「攻撃者が嫌がる面倒な仕組み」を幾重にも積み重ねることだ。IMDSv2の強制は、その中でも最も費用対効果が高く、かつ即効性のある防御策である。

明日と言わず、今すぐ君の環境の http-tokens 設定を確認してくれ。もし optional になっていたら、それが君のシステムの「穴」だ。健闘を祈る。

コメント

タイトルとURLをコピーしました