概要
クロスサイトスクリプティング【Cross-Site Scripting】(以下、XSS)とは、ユーザ⼊⼒に対して動的にページ⽣成するアプリ(Webページ)において、外部から悪意のあるスクリプトを挿⼊されてしまう脆弱性です。 HTMLの⽂法上特別な意味を持つ特殊記号(メタ⽂字)が正しく扱われていないことに起因します。基本的にサーバには影響がなく、利⽤者のブラウザ上で任意のスクリプトが実⾏されるのが特徴です。
XSSにより攻撃者は、Webサイトの改ざん、セッションの乗っ取り、情報窃取、悪意のあるサイトへのリダイレクトなどを⾏うことが可能です。
IPA(情報処理推進機構):ソフトウェア等の脆弱性関連情報に関する届出状況[2023年第1四半期(1⽉〜3⽉)]の2-2-3.脆弱性の種類・影響別届出件数のグラフを⾒ると、そのほとんどをXSSが占めていることから、XSSはエンジニアが作りこみやすい脆弱性であることがわかります。
XSSには、反射型XSS(Reflected XSS)・格納型XSS(Stored XSS)・DOM Based XSSの3種類が存在します。
XSSの概要と反射型XSS(Reflected XSS)・格納型XSS(Stored XSS)については別記事で詳しく解説しています。
この記事ではDOM Based XSSについて解説していきます。
DOMとは?
DOMとはDocument Object Modelの略称で、HTMLやXMLを構成する要素をプログラム上で参照・操作するための仕組みです。
Level 1 Document Object Model Specificationでは、DOMとは「HTMLおよびXMLドキュメント⽤のプログラミングAPIです。ドキュメントの論理構造と、ドキュメントのアクセスおよび操作⽅法を定義します。」と記載されています。
The Document Object Model (DOM) is a programming API for HTML and XML documents. It defines the logical structure of documents and the way a document is accessed and manipulated.
API【Application Programming Interface】とは、異なるアプリケーションやソフトウェアをつなげる仕組みのことです。
ソフトウェアやアプリケーションの⼀部を外部に公開することにより、第三者が開発した別のソフトウェアやアプリケーションの機能を共有することができます。
DOMを使用することで、スクリプトからHTMLやXMLを操作することが可能になります。本記事においては、単に「スクリプト」という場合には、JavaScriptのことを指しています。
DOMツリー
ブラウザはWebページが読み込まれた際に、そのページのDOMを⽣成します。HTMLやXMLから全ての要素を抽出し、最上位の要素(root)を頂点として、下位要素(node)とそれらを結ぶ枝(edge)で構成されており、逆さまにした⽊(Tree)のように⾒えることからDOMツリーと呼ばれています。
[DOMツリー]
ノードの種類 | 概要 |
ドキュメントノード(黄) | DOMツリー構造のルートノードです。HTML文書全体を表します |
要素ノード(青) | HTMLタグを表します |
属性ノード(オレンジ) | HTMLタグ内に記述されている属性を表します |
テキストノード(緑) | タグではない文字の部分を表します |
コメントノード(紫) | コメントを表します |
DOMツリーは次のHTMLから要素を抽出したものになります。
[HTML]
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" lang="ja">
<title>脆弱性ブログ</title>
<!-- コメント -->
</head>
<body>
<h1>一覧</h1>
<p id="blog1">セッションハイジャック</p>
<p id="blog2">セッションの固定化</p>
<p id="blog3">
<font color="red">XSS</font>
</p>
</body>
</html>
また、HTMLをブラウザで表⽰すると次のようになります。
[ブラウザ]
DOMの操作
スクリプトを使⽤してブラウザ内部にあるDOMにアクセスし、内容を変更することができます。DOMが変更されると、⾃動的にブラウザに表⽰されている内容がDOMに合わせて再描画されます。
先ほどのHTMLの 「セッションハイジャック」 の⽂字を黒から⻘に変更させてみます。
body内に下記のスクリプトを記⼊します。
<script>
document.getElementById("blog1").style.color = "blue";
</script>
document.getElementById
は引数に指定されたid属性を持つ要素ノードをDOM全体から検索します。続いて、取得した要素ノードにstyle.color = "blue"
でフォントカラーを⻘に変更します。
<body>
<h1>一覧</h1>
<p id="blog1">セッションハイジャック</p>
<p id="blog2">セッションの固定化</p>
<p id="blog3">
<font color="red">XSS</font>
</p>
<script>
document.getElementById("blog1").style.color = "blue";
</script>
</body>
「セッションハイジャック」の⽂字列が⻘に変わっていることがブラウザから確認できました。
このようにDOMを使⽤することで、Webサーバを介さずスクリプトからHTMLを操作することが可能になります。
DOM Based XSS
DOM Based XSSは、サイト利⽤者のブラウザ上で、JavaScriptがDOMを介してHTMLを操作する際に、意図しないスクリプトを出⼒してしまうXSSです。
反射型XSSおよび格納型XSSは、Webサーバからのレスポンス内に悪意あるスクリプトが含まれています。それに対してDOM Based XSSは、Webサーバからのレスポンスに直接悪意のあるスクリプトが出⼒されるのではなく、クライアントブラウザ上で正規のJavaScriptが実⾏されたタイミングで、初めて悪意のあるスクリプトが出⼒されるという点で異なっています。
反射型XSSおよび格納型XSSと異なり、DOM Based XSSは必ずしもサーバを介す必要がありません。
DOMの仕組みを利⽤することからDOM Based XSSと呼ばれています。
攻撃の⼿段は反射型XSSと似ていますが、反射型XSS・格納型XSSはサーバ上でHTMLを⽣成する際に、ユーザが⼊⼒した⽂字列の扱いが正しくないことが原因で発⽣します。DOM Based XSSは、サーバ上でのHTMLの⽣成には問題はなく、最初に記載した通りブラウザ上で動作するスクリプトのコードに問題があるため発⽣します。
DOM Based XSSはWebアプリでDOMに関係するスクリプトのメソッドを呼び出している箇所で発⽣します。
攻撃例
このページはユーザが⼊⼒した値を結果に表⽰します。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" lang="ja">
<title>dom based xss</title>
<script type="text/javascript">
function click() {
//detail要素を取得します
const detail = document.querySelector("#detail");
//value要素から値(ユーザが入力した値)を取得します
const value = document.querySelector("#value").value;
//api.phpと非同期で通信を行います。valueを送信した後、結果としてレスポンスを受け取り、そのレスポンスからHTMLを作成しdetailの値を置き換えます
fetch("http://localhost/provisional/xss/dom_based_xss/api.php?value=" + value).then((res) => {
res.text().then((text) => {
detail.innerHTML = JSON.parse(text).value;
})
})
}
</script>
</head>
<body>
<h1>入力</h1>
<input type="text" id="value" name="value" value="" size="100">
<input type="button" id="execute" name="execute" value="保存" onclick="window.click();"><br>
<h1>結果</h1>
<div id="detail"><br></div>
</body>
</html>
<input type="button" onmouseover="alert(JSON.stringify(localStorage))" value="gift">
giftというボタンが作成されました。このボタンにカーソルを合わせるとスクリプトが実⾏されます。
⼀⾒、反射型XSSと同じように⾒えますが、ブラウザ上で動作する正規のスクリプトに問題があるため、外部から挿入されたスクリプトが実⾏されています。
このdetail.innerHTML
がスクリプトが含まれる⽂字列をそのまま使⽤しHTMLを⽣成するため、DOM based XSSが可能になります。
以下の図はDOM Based XSSを⽤いた攻撃成⽴までの⼀例になります。
対策
適切なDOM操作関数を使用する
ユーザが操作可能な値をもとにスクリプトでHTMLを組み立てる際は、innerHTMLやdocument.writeなど文字列からHTMLを生成するメソッド・アクセサを使用せず、textContentを使用するようにします。
ユーザが操作可能な値(一部) |
URL(location.href、location.hash等) |
リファラ(document.referrer) |
Cookie(document.cookie) |
他の対策は別記事で詳しく解説しています。
まとめ
クロスサイトスクリプティングの脆弱性が存在していると、外部から悪意のあるスクリプトを挿入する攻撃が可能になります。攻撃が成功すると、攻撃者はWebサイトの改ざん、セッションの乗っ取り、情報窃取、悪意のあるサイトへリダイレクトなどを行う事が可能です。
こういった被害を防ぐには、サイト設計の見直しや脆弱性診断などを行い、安全な状態になっているか確認することをお勧めします。