インジェクション - XSS
クロスサイトスクリプティング(XSS)は、攻撃者が制御するスクリプトを他のユーザーのブラウザで実行させるインジェクション脆弱性の一種です。XSSは、HTML/JavaScriptインジェクション脆弱性と見なすこともできます。
XSS脆弱性の影響は、その脆弱性が存在するコンテキストに大きく依存します。スクリプトが実行されるページからの情報抽出から、ページの状態を編集して被害者になりすまして操作を実行することまで、多岐にわたります。
遭遇する可能性のあるXSSの種類を見ていきましょう。
XSSの種類
XSSは、その特徴を区別するためにいくつかのカテゴリーに分類できます。これらは、ペイロードがどのように配信されるか、そしてエントリーポイントがどこにあるかによって分類されます。
ペイロードの配信ベクトル(保存型 vs 反射型)
攻撃者がXSSペイロードを配信する方法には、主に2つの方法があります。
- 保存型XSS:データベースなどに保存されたユーザー制御データを通じて、他のユーザーにレンダリングされるもの
- 反射型XSS:URLやクエリ文字列を通じて提供されたペイロードが、ユーザーによって閲覧されるもの
重要な違いは、反射型XSSは通常ユーザーの操作に依存し、悪意のあるペイロードを含むリンクを開いたユーザーのみに影響を与える点です。一方、保存型XSSは、ペイロードをレンダリングするページを開くだけで、1人または複数のユーザーに対して実行される可能性があります。
ペイロードの場所(DOM型 vs 非DOM型)
ペイロードがどこに注入されるかによって、その脆弱性が「DOM型」であるかどうかが決まります。これは、ペイロードがどこでレンダリングされるかという違いを指します。
- 非DOM型XSS:タグ内や属性内など、HTML内でペイロードがレンダリングされるもの
- DOM型XSS:`<script>`タグや`onclick=""`のようなイベントハンドラなど、JavaScript内でペイロードがレンダリングされるもの
XSS - 防御の基本原則
このセクションでは、XSS防御の基礎となる原則について説明します。これらの基本を理解することは不可欠ですが、実際には、XSS対策の99%は使用しているテンプレートライブラリに依存すべきです。
ブラウザで実行されるマークアップやコードにレンダリングされるテンプレートを作成する際、XSSを防ぐ鍵は「エンコーディング」です。この文脈におけるエンコーディングとは、一連の文字を、インタープリタによって特定の方法で処理される形式に変換することを意味します。
ただし、使用すべきエンコーディングの種類は、データが使用される場所やコンテキストによって異なります。
- タグ内(例: `<div>ユーザー入力</div>`):**HTMLエンコーディング**
- 属性内(例: `<input placeholder="ユーザー入力"></input>`):**属性エンコーディング**
- JavaScript内(例: `<script>x = "ユーザー入力";</script>`):**JavaScriptエンコーディング**
一部のフレームワークでは、防御の主要手段としてエンコーディングを行わず、代わりにサニタイズ(無害化)を利用して、危険な可能性のあるコンテンツを値から取り除きます。これは非常に複雑なプロセスであり、考慮すべき多くのエッジケースが存在します。独自のサニタイズルーチンを実装することは推奨されません。
例
さまざまな言語の例を見て、実際にどのような動作になるのかを確認してみましょう。
C# - 安全でない例: Razor
`IHtmlContent` オブジェクトの前に `@` を付けると、その値はエンコードされることなく直接テンプレートに挿入されます。
<!--- 安全ではありません: htmlSnippet はエスケープされずに解釈されます --->
@Html.Raw(htmlSnippet)
C# - 安全な例: Razor
Razorテンプレートでは、デフォルトで `@` が付いた `string` はすべてHTMLエスケープされます。
<!--- 安全: htmlSnippet はHTMLエスケープされます --->
@htmlSnippet
Java - 安全な例: JSP
`c:out` を使用するとデフォルトでXMLエスケープが行われます(`escapeXml` プロパティで変更可能)。これによりHTMLコンテキストでのXSSを防ぐことができますが、他のコンテキストでは保護されません。
<div><c:out value="<%= author %>" /></div>
Java - 安全な例: (fnxml)
Similar to above, you can also directly call `fn:escapeXml`, which will XML escape the input given to it. This will also only protect in a HTML context.
<div>${fn:escapeXml(author)}</div>
Javascript - 安全でない例: Angular innerHtml
`innerHTML` プロパティを指定すると、その名の通り出力エンコーディングが無効化されるため、XSSのリスクにさらされることになります。
<!--- 安全ではありません: htmlSnippet はエスケープされずに解釈されます --->
<p [innerHTML]="htmlSnippet"></p>
Javascript - 安全な例: Angular 補間
二重中括弧(`{{` および `}}`)を使用したAngularのテキスト補間では、出力がHTMLエスケープされるため、XSSから保護されます。
<!--- 安全: htmlSnippet はエンコードされた後に解釈されます --->
<p>{{htmlSnippet}}</p>
Javascript - 安全でない例: React dangerousInnerHtml
`dangerouslySetInnerHTML`プロパティを指定すると、その名の通り、出力エンコーディングが無効化されるため、XSSのリスクにさらされることになります。
<!--- 安全ではありません: その名の通り、dangerouslySetInnerHTML属性は出力をエスケープしないため危険です --->
<div dangerouslySetInnerHTML={{ __html: htmlSnippet }} />;
Javascript - 安全: Reactの補間
Reactで波括弧(`{`および`}`)を使用してテキストを補間すると、出力がHTMLエスケープされるため、XSSから保護されます。
<!--- 安全: htmlSnippetはエンコードされた後に解釈されます --->
<div>{htmlSnippet}</div>
Python - 安全ではありません: Django
Djangoテンプレートで`safe`フィルターを使用すると、出力の自動エスケープが無効になり、XSSに対する保護が機能しなくなります。
<!--- 安全ではありません: htmlSnippetはHTMLエンコードされません --->
<div>{{ htmlSnippet | safe }}</div>
Python - 安全: Django
Djangoで二重波括弧(`{{`および`}}`)を使用してテキストを補間すると、出力がHTMLエスケープされるため、XSSから保護されます。
<!--- 安全: htmlSnippetはHTMLエンコードされます --->
<div>{{ name }}</div>
実践的なセキュアコーディング
OWASP Top 10のようなソフトウェアの脆弱性を理解し軽減するためのセキュアコーディングガイドラインを確認しましょう。また、実践的なアプリシミュレーションを通じて学べるトレーニングミッションにもぜひ挑戦してください。