XSS(クロスサイトスクリプティング)対策の基本|エスケープの正しい理解

コメント欄、プロフィール表示、検索結果の表示など、「ユーザーが入力した文字をそのまま画面に出す」機能は、Webアプリにはほぼ必ず存在します。

このとき、入力された文字を無防備にHTMLへ流し込んでしまうと、そこにスクリプトが仕込まれ、他の利用者のブラウザで実行されてしまいます。これがXSS(クロスサイトスクリプティング)です。

関連

セキュリティ対策を横断的に確認したい方は、 Webアプリの脆弱性・セキュリティ対策まとめ もご覧ください。

XSSとは何が起きているのか

例えば、コメント投稿機能を考えます。ユーザーが入力した文字列を、そのまま次のようにHTMLへ埋め込んでいたとします。

<div>入力された文字列</div>

ここに次のような文字列が入力されたらどうなるでしょうか。

<script>alert('XSS')</script>

ブラウザはHTMLとしてこれを解釈するため、<script>タグの中身をそのまま実行してしまいます。今回はalertですが、実際の攻撃ではCookieを盗み取ってログインセッションを乗っ取るページの内容を書き換えて偽の入力フォームを表示するといった悪用がされます。

<script>タグを禁止しているつもりでも、<img src="x" onerror="...">のように、タグの属性からスクリプトを実行する方法もあるため、「特定のタグだけ弾く」という対策では不十分です。

体験|入力した内容がどう表示されるか見る

実際に手を動かして確かめてみましょう。以下のデモの「エスケープなし」側は、安全のため親ページから隔離された枠(iframe)の中だけで動作します。この記事のページ自体やあなたのCookieに影響することはありません。

体験デモ|入力した内容がどう表示されるか見る

エスケープなしエスケープありを見比べる

左側は安全のため、隔離された枠(iframe・親ページのCookieや情報には一切アクセスできません)の中だけで実行されます。実際にタグを含む文字を入力して、表示のされ方の違いを確認してください。

エスケープなし(危険・隔離枠内で実行)
エスケープあり(安全・文字として表示)
エスケープ後、実際にHTMLへ出力される文字列

「エスケープなし」側は、<script>onerrorがそのまま実行され、意図しない挙動を起こしていたはずです。一方「エスケープあり」側は、タグがタグとして解釈されず、文字そのまま(&lt;script&gt;のような形)に変換されて画面に表示されています。これがエスケープの効果です。

対策の基本|出力時にエスケープする

XSS対策の基本は、ユーザー入力をHTMLとして出力する直前に、必ずエスケープ処理を行うことです。

エスケープとは、HTMLとして特別な意味を持つ文字(< > & " 'など)を、意味を持たない文字表現(&lt;など)に置き換える処理です。

多くのテンプレートエンジン(Django テンプレート、JinjaなどのPython系、Reactなど)は、デフォルトで自動的にエスケープを行う設計になっています。危険なのは、この自動エスケープを意図的に無効化する機能(Djangoの|safeフィルタ、ReactのdangerouslySetInnerHTMLなど)を、必要のない場面で使ってしまうケースです。

「サニタイズ」との違い

エスケープと混同されやすい概念に「サニタイズ」があります。サニタイズは、危険な要素(タグやスクリプト)そのものを取り除く処理で、リッチテキストエディタのように「一部のHTMLタグだけは許可したい」場面で使われます。

一方エスケープは、タグを取り除くのではなく「タグとして解釈させない」処理です。単なるテキストとして表示したいだけなら、基本的にはエスケープだけで十分です。両者の違いについては、 入力バリデーション・サニタイズ・エスケープの違い でも整理しています。

実務で意識したいポイント

まとめ

XSSは、ユーザー入力をそのままHTMLとして出力してしまうことで発生します。対策の基本は出力時のエスケープであり、多くのフレームワークではデフォルトで有効になっています。

まずは自分のアプリの中で「自動エスケープを無効化している箇所」がないかを見直すところから始めてみてください。

顧問契約、開発、プロジェクトのご依頼を承ります。
内容、期間により金額が変動しますのでお気軽にお問い合わせください。

※ 案件によりお受けできない場合もあります。

お問い合わせはこちら →
戻る