SSRF攻撃の仕組みと効果的な緩和策

Tech

本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

SSRF攻撃の仕組みと効果的な緩和策

SSRF (Server-Side Request Forgery) 攻撃は、Webアプリケーションがユーザー指定のURLに基づいてサーバー側でリクエストを生成する機能を持つ際に発生する深刻な脆弱性です。攻撃者はこの脆弱性を悪用し、サーバー自身やサーバーがアクセス可能な内部ネットワーク上の任意のサービスに対して、不正なリクエストを偽装・実行させることが可能になります。

脅威モデル

SSRF攻撃は、外部から直接アクセスできない内部システムへの侵入経路を提供するため、多くのWebアプリケーションにとって重大な脅威となります。

SSRFの定義: SSRFは、脆弱性を持つWebサーバーが、攻撃者の指定したURLに対してHTTP/HTTPSリクエストを生成してしまうことで、サーバーの内部ネットワークにあるリソースやクラウドプロバイダーのメタデータサービスに不正アクセスが行われる攻撃です[1]。

攻撃対象と目的:

  • 内部ネットワークの探索と悪用: 外部から隔離された内部Webサーバー、データベース、キャッシュサーバー、管理パネル、APIエンドポイントなどへのアクセスを試み、情報漏洩や不正操作を行います。

  • クラウドメタデータサービスの認証情報窃取: AWS EC2、GCP Compute Engine、Azure VMなどのクラウドインスタンスが提供するメタデータサービスから、一時的な認証情報(IAMロールのクレデンシャルなど)、設定情報、APIキーなどを窃取します。

  • ファイルシステムへのアクセス: file:// スキームなどを利用して、サーバー上のローカルファイル(例: /etc/passwd、設定ファイルなど)を読み取ります。

  • 各種プロトコルの悪用: Gopher、FTP、SMTP、Redisなどの様々なプロトコルを利用し、内部サービスに対して任意のコマンドを実行させたり、メッセージを送信させたりします[5]。

攻撃シナリオ

SSRF攻撃は、多岐にわたる内部リソースへの不正アクセスを可能にし、情報窃取からシステム制御まで広範な影響を及ぼします。以下に代表的な攻撃チェーンとシナリオを示します。

graph TD
    A["攻撃者"] -->|悪意のあるURLを挿入| B("脆弱なWebアプリケーション")
    B -->|内部リクエスト生成 (SSRF)| C{"内部リソース/外部サービス"}
    C -->|機密情報/操作| D["攻撃者のコントロールするサーバー"]
    C -->|インスタンスメタデータサービス| E["クラウドの一時認証情報"]
    C -->|内部管理システム| F["機密データ/管理機能"]
    C -->|ローカルファイルシステム| G["設定ファイル/パスワード"]

    subgraph 攻撃フェーズ
        A --> B
        B --> C
    end

    subgraph 悪用フェーズ
        C --> D
        C --> E
        C --> F
        C --> G
    end

    style A fill:#fce,stroke:#f8c,stroke-width:2px,color:#333
    style B fill:#fce,stroke:#f8c,stroke-width:2px,color:#333
    style C fill:#ccf,stroke:#99f,stroke-width:2px,color:#333
    style D fill:#fdd,stroke:#fbb,stroke-width:2px,color:#333
    style E fill:#fdd,stroke:#fbb,stroke-width:2px,color:#333
    style F fill:#fdd,stroke:#fbb,stroke-width:2px,color:#333
    style G fill:#fdd,stroke:#fbb,stroke-width:2px,color:#333
  • 内部ネットワークへのアクセス: 攻撃者は、http://localhost/adminhttp://192.168.1.100/status のような内部IPアドレスやホスト名を含むURLをアプリケーションに送信します。アプリケーションはこれを受け取り、本来アクセスすべきでない内部システムへのリクエストをサーバーサイドで実行し、その応答を攻撃者に返してしまうことで、内部リソースが露呈します。

  • クラウドメタデータサービスへのアクセス: クラウド環境では、インスタンスに関する情報(一時的なIAMロール認証情報、ホスト名、ネットワーク設定など)を提供するメタデータサービスが存在します。AWSのEC2インスタンスメタデータサービス (IMDS) V1は、シンプルなHTTPリクエスト http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name で認証情報が取得できました[3]。SSRF脆弱性を持つアプリケーションは、このURLにリクエストを送信させられることで、インスタンスのIAMロールに関連付けられた認証情報を攻撃者に漏洩させてしまいます。

    IMDSv1 (誤用例):

    # IMDSv1の場合、直接認証情報を取得可能 (SSRFで悪用される可能性)
    
    
    # AWS CLIがインストールされた環境で実行されることを想定。
    
    
    # このコマンドは、EC2インスタンスがSSRF脆弱性を持つ場合に、攻撃者が内部から実行させることで認証情報を窃取するシナリオを想定しています。
    
    curl http://169.254.169.254/latest/meta-data/iam/security-credentials/your-role-name
    

    安全な代替としてのIMDSv2: AWSは2019年11月26日 (JST) にIMDSv2をリリースしました[3]。IMDSv2では、最初に一時的なトークンを取得し、そのトークンをHTTPヘッダーに含めて実際のメタデータリクエストを行う必要があります。これにより、シンプルなSSRF攻撃ではトークンを取得できず、認証情報の漏洩を防ぐことができます。

    IMDSv2 (安全な代替):

    # IMDSv2の場合、まずトークンを取得
    
    
    # トークンはPUTリクエストで取得し、有効期限(TTL)を設定します。
    
    
    # このトークンは単一のセッションに紐づけられ、再利用が困難です。
    
    TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
    
    # 取得したトークンをヘッダーに含めてメタデータをリクエスト
    
    
    # トークンがないリクエストや不正なトークンのリクエストは拒否されます。
    
    curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/your-role-name
    
  • ファイルシステムへのアクセス: file:///etc/passwdfile:///etc/shadow のようなURLを悪用することで、サーバー上の機密性の高いシステムファイルを読み取らせることが可能です。

  • プロトコル悪用(Gopher/FTPなど): Gopherプロトコルを悪用すると、SSRF経由でFTP、SMTP、Redisなどのサービスに任意のコマンドを送信できます[5]。例えば、SMTPサーバーに偽のメールを送信したり、Redisインスタンスにコマンドを実行させたりすることが可能です。

    PythonでのGopherプロトコル悪用例 (誤用例):

    import requests
    
    # GopherプロトコルでSMTPサーバーに接続し、偽のメールを送信する例
    
    
    # このURLを脆弱なアプリケーションが処理すると、内部のSMTPサーバーにメールが送信される可能性があります。
    
    
    # 注意: requestsライブラリは通常Gopherプロトコルを直接サポートしません。
    
    
    # 実際にはurllibなどのlow-levelなHTTPクライアントや特定のフレームワークがGopherをサポートし、
    
    
    # SSRFに利用されやすいケースがあります。
    
    malicious_gopher_url = (
        "gopher://localhost:25/_"  # _ はRFC上の要件
        "HELO%20attacker.com%0D%0A"
        "MAIL%20FROM:%3Cattacker@example.com%3E%0D%0A"
        "RCPT%20TO:%3Cvictim@example.com%3E%0D%0A"
        "DATA%0D%0A"
        "Subject:%20SSRF%20Test%0D%0A"
        "Hello,%20this%20is%20an%20SSRF%20test.%0D%0A"
        ".%0D%0A"
        "QUIT%0D%0A"
    )
    
    print(f"Malicious Gopher URL (example): {malicious_gopher_url}")
    
    # 通常のrequestsライブラリではGopherプロトコルは扱えません。
    
    
    # したがって、以下のコードはRequestsConnectionErrorを発生させます。
    
    
    # しかし、このURLがurllibなどの他のライブラリや特定の環境下で処理された場合、
    
    
    # 意図しない内部リソースへのリクエストが実行される可能性があります。
    
    
    # try:
    
    
    #    response = requests.get(malicious_gopher_url, timeout=5)
    
    
    #    print(response.text)
    
    
    # except requests.exceptions.RequestException as e:
    
    
    #    print(f"Error making request (expected for standard requests library): {e}")
    
    # 対策: アプリケーションは、URLのスキームを厳格に検証し、許可されたスキーム (http, https)
    
    
    # のみをホワイトリスト化することが必須です。
    

検出と緩和策

SSRF攻撃からシステムを保護するためには、多層的なアプローチが不可欠です。

  1. 入力検証(ホワイトリスト方式の採用): ユーザーから受け取るURLやホスト名に対して、厳格な検証を行います。

    • ホワイトリスト: 許可されたドメイン、IPアドレス、URLスキーム(httphttpsのみなど)を明確に定義し、それ以外を拒否するのが最も安全なアプローチです[1]。内部IPアドレス範囲 (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.1/8) へのアクセスは、原則として拒否します。

    • ブラックリストの限界: 内部IPアドレスや予約済みIPアドレスをブラックリスト化するアプローチは、バイパス技術(例: 0.0.0.0, [::], IPアドレスのエンコーディング、DNSリバインディングなど)が存在するため、安全ではありません。

    Pythonでの安全なURL検証 (安全な代替):

    from urllib.parse import urlparse
    import socket
    
    def is_safe_url(url: str) -> bool:
        """
        URLが安全な内部リソースまたは許可された外部リソースを指しているか検証します。
        ホワイトリスト方式を採用し、特定のホストとスキームのみを許可します。
    
        Args:
            url (str): 検証するURL文字列。
    
        Returns:
            bool: URLが安全であればTrue、そうでなければFalse。
    
        計算量: URLパースは定数時間。socket.gethostbyname()はネットワークI/Oを含むため変動。
        メモリ: 低。
        前提: 許可するホストリスト `allowed_domains` が事前に定義されていること。
              DNSルックアップを行うため、ネットワークアクセスが必要です。
        """
        allowed_schemes = ['http', 'https']
        allowed_domains = ['api.example.com', 'trusted.cdn.com'] # 許可されたドメインのホワイトリスト
    
        try:
            parsed_url = urlparse(url)
    
            # 許可されたスキームであるかチェック
    
            if parsed_url.scheme not in allowed_schemes:
                return False
    
            # ホストが存在しないか、不正な形式であれば拒否
    
            if not parsed_url.hostname:
                return False
    
            # 許可されたドメインのホワイトリストに存在するかチェック
    
            if parsed_url.hostname in allowed_domains:
                return True # 明示的に許可されたドメイン
    
            # IPアドレスへの直接アクセスを許可しない場合 (一般的には推奨)
    
    
            # DNSルックアップを行い、ホスト名から解決されたIPアドレスが内部IPでないことを確認
    
            ip_address = socket.gethostbyname(parsed_url.hostname)
    
            # 内部IPアドレス範囲のチェック (RFC 1918 + loopback)
    
            if (ip_address.startswith('10.') or
                ip_address.startswith('172.16.') or # 172.16.0.0/12
                ip_address.startswith('192.168.') or
                ip_address == '127.0.0.1' or
                ip_address == '0.0.0.0'): # 特定のクラウド環境でメタデータサービスが0.0.0.0でListenする場合があるため追加
                return False # 内部IPアドレスは拒否
    
            # その他の未許可のドメインは拒否
    
    
            # ホワイトリストにない外部ドメインもデフォルトで許可しない厳格な方針
    
            return False 
    
        except (ValueError, socket.gaierror):
    
            # 無効なURL形式、またはホスト名解決に失敗した場合
    
            return False
    
    # 使用例
    
    print(f"Is 'https://api.example.com/data' safe? {is_safe_url('https://api.example.com/data')}") # True
    print(f"Is 'http://malicious.com/exploit' safe? {is_safe_url('http://malicious.com/exploit')}") # False
    print(f"Is 'http://127.0.0.1/admin' safe? {is_safe_url('http://127.0.0.1/admin')}") # False
    print(f"Is 'file:///etc/passwd' safe? {is_safe_url('file:///etc/passwd')}") # False
    
  2. ネットワークレベルの制限:

    • ファイアウォール: アプリケーションサーバーからの送信リクエストを制限するファイアウォールルールを設定します。特に、内部ネットワークのアドレス範囲、クラウドメタデータサービスIP 169.254.169.254 (AWS), 169.254.169.254 (GCP), 169.254.169.254 (Azure) へのアクセスをブロックします[4]。

    • WAF (Web Application Firewall): WAFは、SSRF攻撃のシグネチャ(例: 169.254.169.254のようなメタデータサービスIPアドレス、file://などのプロトコル)を検出し、ブロックできます。ただし、バイパス技術も存在するため、WAFのみに依存せず多層防御を構築すべきです[1]。

  3. IAMと最小権限の原則: アプリケーションサーバーがクラウド環境のインスタンスメタデータサービスから取得できる権限を最小限に抑えます。インスタンスに付与するIAMロールは、必要最低限のアクセス権限のみを持つように設計し、機密情報へのアクセス権を付与しないことが重要です。

  4. メタデータサービス (IMDSv2) の利用: AWSを利用している場合、EC2インスタンスのメタデータサービスは必ずIMDSv2を強制するように設定します[3]。これにより、SSRF攻撃による一時認証情報の漏洩リスクを大幅に低減できます。

  5. 応答検証: 外部リソースから取得したデータが、アプリケーションが期待する形式や内容であるかを検証します。不正なMIMEタイプや、過度に大きいデータ、予期しないコンテンツが含まれていないかを確認することで、間接的なデータ漏洩やサービス妨害を防ぎます。

運用対策と現場の落とし穴

  1. 鍵/秘匿情報の取り扱い:

    • シークレットマネージャーの利用: APIキー、データベースパスワードなどの秘匿情報は、ソースコードにハードコーディングせず、AWS Secrets Manager、Azure Key Vault、Google Secret Managerなどのシークレットマネージャーで一元管理します。

    • 最小権限の付与: シークレットマネージャーへのアクセス権限も、必要最小限のサービスアカウントやIAMロールにのみ付与します。

    • 定期的なローテーション: 秘匿情報は定期的に(例えば90日ごとに)ローテーションすることで、漏洩時の影響範囲を限定します。

    • 監査ログの監視: シークレットへのアクセスログを監視し、不審なアクセスパターンを検知します。

  2. ログ監視と異常検知:

    • アプリケーションのアクセスログ、エラーログ、WAFログなどを集中ログ管理システム(Splunk, ELK Stack, Datadogなど)で収集・分析します。

    • SSRF攻撃パターン(例: 内部IPアドレスへのリクエスト、file://スキームの使用、特定のメタデータサービスIPへのアクセス試行)に対するアラートを設定し、早期に攻撃を検知します。

  3. セキュリティテスト:

    • SAST (Static Application Security Testing): コードレビューや静的解析ツールを用いて、SSRFの脆弱性につながる可能性のあるコードパターン(ユーザー入力からURLを構成する部分など)を開発段階で特定します。

    • DAST (Dynamic Application Security Testing): 動的解析ツールやペネトレーションテストを実施し、稼働中のアプリケーションに対してSSRF攻撃をシミュレートし、脆弱性を発見します。

  4. 現場の落とし穴:

    • 誤検知と運用負荷: 厳しすぎる入力検証ルールやWAFの設定は、正当なリクエストを誤ってブロックし、サービスの可用性を低下させる可能性があります。セキュリティと運用のバランスを見極め、定期的なレビューと調整が必要です。

    • 検出遅延: 攻撃パターンが巧妙化すると、WAFやログ監視だけではSSRF攻撃の検出が遅れることがあります。振る舞い検知やAIを活用した異常検知システム導入も検討し、多角的なアプローチで検出精度を高める必要があります。

    • 可用性トレードオフ: SSRF対策としてのネットワーク制限やプロキシ導入は、アプリケーションのパフォーマンスに影響を与える可能性があります。特にレイテンシに敏感なシステムでは、慎重な設計とテストが求められます。

まとめ

SSRF攻撃は、Webアプリケーションを通じて内部リソースやクラウドメタデータに不正アクセスを許す深刻な脆弱性です。この脅威に対抗するためには、ホワイトリスト方式による厳格な入力検証、IMDSv2のような安全なプロトコルの採用、ネットワークレベルでのアクセス制限、そして秘匿情報の適切な管理と堅牢な監視体制が不可欠です。本記事で解説した多層的な防御策と運用上の注意点を理解し、実践することで、SSRF攻撃のリスクを大幅に低減できます。


参照: [1] OWASP Foundation. (2024年4月28日). Server-Side Request Forgery. https://owasp.org/www-community/attacks/Server_Side_Request_Forgery [2] MITRE. (2023年7月20日). CWE-918: Server-Side Request Forgery (SSRF). https://cwe.mitre.org/data/definitions/918.html [3] AWS Security Blog. (2023年10月11日). Protecting against SSRF with IMDSv2. https://aws.amazon.com/blogs/security/protecting-against-server-side-request-forgery-with-imdsv2/ [4] Google Cloud. (2024年2月20日). Best practices for preventing SSRF. https://cloud.google.com/architecture/best-practices-for-preventing-server-side-request-forgery [5] PortSwigger. (2024年1月19日). What is SSRF? Server-Side Request Forgery explained. https://portswigger.net/web-security/ssrf

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

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