XSS(Cross Site Scripting)とは
XSSとは、Webサイト・Webアプリケーションへの攻撃手法の一つです。
数ある攻撃手法の中でも、
「HTMLの中に悪質なコードを埋め込み、Webサイト利用者の情報を抜き取ったり、Webサイト利用者の環境で悪質なコードを実行すること」
を指します。
攻撃対象として狙われやすいのが、
- ユーザーから入力を受け付け、それを元にページを表示するWebサイト
- アンケートサイト
- 掲示板サイト
- ブログ etc.
- Webアプリケーション
- ログイン機能を持つWebアプリ
- カスタムHTMLを埋め込むことができるWebアプリ
- 管理画面・社内ツール
- カスタマーサポート用の管理画面
- 「身内向け」の閉じた&重要な情報が関与するページ
- 動的なDOM操作が多いSPA(Single Page Application)
- URLのフラグメント(#以降の情報)を利用して、URL中のパラメータを元にHTMLを描画するアプリ
- リッチテキストエディタ(ユーザーが装飾したテキストをそのまま描画する仕組み)を搭載するアプリ
などなどです。
IPA(情報処理推進機構)への脆弱性に対する届出では、クロスサイトスクリプティングの脆弱性が常に上位に挙げられるほど、Webサイトへの主要な攻撃手法であり、かつエンジニアが気をつけなければならないセキュリティ脆弱性です。
XSS攻撃の主要3パターン
とはいっても、具体的にどんなやり方で攻撃されるのかイメージしづらい人もいると思うので、いくつか主要な攻撃パターンをお示しします。
1. 反射型XSS
反射型XSS(Reflected-XSS)は、ユーザーが特定のリンクをクリックしたときに被害が発生します(最も古典的でケースも多い手法)。
流れとしては、
- 攻撃者が、用意した悪意のあるURLを他の利用者画面に表示
- 利用者が間違えてそのURLをクリック
- webサーバーに対して、攻撃者による悪質なコードが含まれているリクエストが送信
- サーバーがその悪質なコードを、そのままレスポンスとして利用者に返送
- 利用者のブラウザが、「反射」してきた悪質なコードを、正規のコンテンツとして実行してしまう
というパターン。
例えば、
ECサイトの検索機能などで、「<検索キーワード> の検索結果」と表示される箇所があるとき、
検索キーワードに「」を入れることで、利用者が検索結果画面を開いた瞬間にscriptタグの中身が実行され、個人情報が盗まれるということが発生します。
2. 格納型XSS
格納型XSS(Stored XSS または Persistent XSS)は、悪質なスクリプトがWebサーバー(データベース)に**「格納」**され、ページを閲覧した不特定多数に被害が及ぶ手法です。
流れとしては、
- 攻撃者が、掲示板やSNSなどの投稿フォームに悪質なスクリプトを入力して送信
- Webサーバーが、そのスクリプトをデータベースに保存(格納)
- 一般の利用者が、その投稿が含まれるページにアクセス
- サーバーはデータベースから悪質なスクリプトを読み込み、HTMLとして利用者にレスポンスとして返す
- ページを表示したすべての利用者のブラウザで、悪質なスクリプトが実行される
というパターンです。
例えば、
SNSのプロフィール欄やブログのコメント欄。反射型とは異なり、一度罠を仕掛けられると、攻撃者がサイトを去った後でも、そこを訪れたユーザー全員が被害に遭うため、非常に影響範囲が広くなります。
3. DOMベースXSS
DOMベースXSS(DOM-based XSS)は、サーバーを介さず、ブラウザ側のJavaScriptが「DOM(HTML要素の構造)」を動的に書き換える際に発生する手法です。
流れとしては、
- 攻撃者が、URLの「#(フラグメント)」以降などに悪質なスクリプトを含めたリンクをターゲットに送る。
- 利用者がそのURLをクリックする。
- 利用者のブラウザがサーバーへリクエストを送る(※多くの場合、# 以降の内容はサーバーへは送られない)。
- サーバーから返ってきた正規のJavaScriptが、URL内のスクリプト部分を読み取る。
- JavaScriptが innerHTML などを使って、その内容をそのままページ内に描画し、スクリプトが実行される。
というパターン。
例えば、
「mypage.html#name=ゲスト」というURLに対し、JavaScriptが「こんにちは、ゲストさん」と画面に表示する仕組みがある場合。URLを「#name=」に変えることで、サーバーの脆弱性とは無関係に攻撃が成立します。
エンジニアがすべき対策
こういった攻撃に対し、「ただ気をつけよう」ではダメです。開発者側のフレームワークなどで用意されている技術的な手法を使って、あらゆる攻撃を想定して守ることが重要です。
主要な対策として4つのレイヤーで整理します。
1. 出力時のエスケープ処理
XSS対策の鉄則は、「出力する直前に、HTMLとして意味を持つ文字を無害化する」ことです。これをエスケープと呼びます。
エスケープとは、HTMLで使われる>や&、"といったあらゆる記号をエスケープ(別の文字列に変換)して無効化することです。
例えば、
| 文字 | エスケープ後 | 理由 |
|---|---|---|
| < | < |
タグの開始を阻止するため |
| > | > |
タグの終了を阻止するため |
| & | & |
エンティティ参照を阻止するため |
| " | " |
属性値の書き換えを阻止するため |
| / | / |
閉じタグの形成を阻止するため |
などの対応関係があります。
これを活用することで、もし仮に攻撃者から
<script>悪意のあるコード</script>
のようなコードが埋め込まれたとしても、エスケープ処理によって
<script>悪意のあるコード</script>
と変換されるため、これはもはやscriptを実行できる状態ではなくなるというわけです。
各言語によって、エスケープ処理の仕方やメソッドは異なるため調べてみると実践的に役立つと思います!
2. 安全なAPI・フレームワークの選択
先ほど示したような処理で直接コードを無効化することも重要ですが、より確実なのは「そもそもそんな危ない書き方をしない」ということです。
例えば、
- javascriptで要素を書き換える際は、特別HTMLとして出力したい理由がない限りは
.innnerHTMLではなく.textContentを使う - テンプレートエンジンを活用する
- React, Vue, Anglarなどのモダンフレームワークは、デフォルトで変数をエスケープして出力する仕組みがある
などです。
3. ユーザー入力HTMLのサニタイズ
ブログのエディタや、ユーザーにHTMLをカスタマイズさせる場合などは、どうしてもユーザーにHTML入力を許可したい、というパターンもあります。
その際は、単純なエスケープではなく、許可したタグ以外を除去するライブラリを活用することができます。
エスケープはすべての記号を文字列として扱うのに対し、
サニタイズはHTMLとして意味を持たせつつ危険な要素・タグだけを排除する処理を指します。
例えば、リッチなテキストエディタを提供したい場合は、
- 太字()やリンク()など、一部のHTMLタグの使用をユーザーに許可
- スクリプト(