XSSについて 開 発 者 が 知 っておくべきこと
目 次 XSSを 防 ぐために: 開 発 者 のための 実 践 ガイド 3 XSS を 避 けるために 知 っておくべき3つの 事 3 HTML のエスケープでは 十 分 ではない 3 フレームワークはあまり 助 けてくれない 3 ネストしたコンテキストに 心 が 折 れる 3 Coverity Security Library(コベリティセキュリティライブラリ) 4 インストールと 使 用 方 法 4 対 処 方 法 4 通 常 のHTML 要 素 4 HTML 属 性 5 HTML 属 性 内 部 のJavaScript 文 字 列 5 HTML 属 性 内 のフルURL 5 HTML 属 性 内 のCSS 文 字 列 6 JavaScriptの 文 字 列 6 JavaScriptの 文 字 列 内 のHTML 7 JavaScriptの 数 値 7 CSS 文 字 列 7 CSS 文 字 列 内 のURL 8 CSSカラー 8 HTML 属 性 内 のURLフラグメント 8 URLクエリ 文 字 とパス 要 素 9 コベリティについて 9 2
XSSを 防 ぐために: 開 発 者 のための 実 践 ガイド このドキュメントでは Webアプリケーション 開 発 時 に 必 要 とされるクロスサイトスクリプティング(XSS)への 対 処 方 法 特 に 開 発 者 が 理 解 しておくべき 修 正 方 法 について 解 説 します まず 前 提 となる 情 報 としてHTMLコンテキストについてご 紹 介 し 次 に Coverity Security Library について そし て web ページ 内 で 動 的 データが 現 れる 13 の 標 準 的 なケースについて 見 ていきます XSS を 避 けるために 知 っておくべき3つの 事 クロスサイトスクリプティング(XSS)は 多 くの 流 動 的 な 要 素 を 含 む 複 雜 な 問 題 ですが ここではもっとも 重 大 な 落 とし 穴 TOP 3 に 注 目 してみます HTMLのエスケープでは 十 分 ではない HTMLのエスケープ(HTMLエンティティを 用 いたもの)が HTMLページ 内 の 動 的 データ 出 力 に 対 して 常 に 正 しい 解 決 策 ではないということを 理 解 しているのは 重 要 なポイントです 全 ての 可 能 なHTML 出 力 コンテキストにおいて 動 的 デ ータを 安 全 にすることができる 魔 法 のエスケーパーというものは 存 在 しません 動 的 データがHTMLのどの 部 分 に 書 き 込 まれるか(たとえば URL JavaScript 文 字 列 など)に 応 じて エスケープやサニタイズ( 無 害 化 )を 使 用 する 必 要 が あります フレームワークはあまり 助 けてくれない フレームワークは 一 般 的 にエスケーパーを 備 えています しかしながら セキュリティにフォーカスした Coverity Security Library(CSL:コベリティセキュリティライブラリ)のようなものでないかぎりは 必 要 なすべてのエスケーパーは 提 供 さ れていないのが 現 実 です たとえば Spring フレームワークでは CSS 文 字 のエスケーパーを 見 つけることができない はずです 彼 らが 装 備 している HTML エンティティや JavaScript 文 字 のエスケーパーは 重 要 かもしれない 全 ての 文 字 やセキュリティのために 設 計 されてはいないのです ネストしたコンテキストに 心 が 折 れる XSS に 対 処 するために 理 解 しておくべき 重 要 なコンセプトがネストした HTML コンテキストです HTML ページ 内 の 特 定 の 出 力 に 対 する ネストしたコンテキストの 積 み 重 ねと どのようなエスケープが 必 要 か 決 定 する 過 程 に 例 をみること ができます 次 のHTMLの 一 部 分 を 考 察 してみましょう : <a href="javascript:hello('${content}')">... この 例 では content が 動 的 データとしてJavaの 式 言 語 (EL)の 表 記 (${content} が 注 目 すべきデータです)を 使 用 してHTMLページに 書 き 込 まれています content が 追 加 された 箇 所 を 分 析 してみると content が 以 下 のように 包 含 されていることがわかります : HTML 属 性 : で 囲 まれた 属 性 の 値 URL: href 属 性 の 内 部 のため ブラウザのためのURLとなることを 想 定 JavaScript: javascript: スキーマであることがわかり そしてその 後 にJavaScriptが 続 くことが 考 えられる JavaScript 文 字 列 : content は hello() JavaScript 関 数 に 渡 される 文 字 列 に 含 まれる HTML コンテキストを 理 解 するためには ブラウザがこのJavaScript 内 のcontentから 得 るものが 何 なのかを 見 てい く 必 要 があります : 1. HTML パーサーが href 属 性 の 内 容 とエスケープされていないHTMLエンティティを 得 る 2. URLをパースし それが javascript: スキーマであることを 認 識 3. URLの 内 容 を 取 得 (javascript: 以 降 )し それをURLデコードする 3
4. URLの 内 容 をJavaScriptに 渡 す 5. JavaScriptパーサーが 実 行 され 我 々のcontentを 含 むJavaScript 文 字 を 取 得 6. JavaScript 文 字 列 の 内 容 が 文 字 列 エスケープシーケンスのために 処 理 される:JavaScript string decoding これらのステップはブラウザがどのようなデコーディングを 行 うかを 示 しています したがって あなたはHTMLコンテキ ストの 積 み 重 ねの 中 で 内 容 が 安 全 に 構 築 されているかを 逆 の 順 序 で 確 かめる 必 要 があります : 1. クォートされた HTML 属 性 2. URL 3. JavaScript 文 字 列 もし あなたが 安 全 に content を 出 力 したいのであれば 以 下 のようにする 必 要 があるでしょう : <a href="javascript:hello('${cov:htmlescape(cov:uriencode(cov:jsstringescape(content)))}')" >... では どうやれば どのコンテキストでエスケープが 必 要 なのかわかるのでしょうか? HTML5 のフルパーサーと 同 じこと を 頭 でする 必 要 がありますか?ラッキーなことに 答 えはノーです あなたが 遭 遇 するであろう ほとんどのすべての 状 況 で 本 当 に 理 解 し 考 慮 しなければならないのは13の 重 要 なコンテキストです 本 ドキュメントの 残 りではこれらについて 見 ていきます もし あなたが 動 的 なデータをここでカバーされていないコンテキストに 挿 入 したいのであれば あなたの 身 近 なセキュ リティエキスパートに 相 談 してください ひとりで 解 決 しようとブラウザをもてあそぶのはやめましょう これは 真 にきち んと 確 認 すべき 事 柄 だからです Coverity Security Library(コベリティセキュリティライブラリ) Coverity Security Library (CSL) は 動 的 なHTMLやSQLデータを 扱 う 場 合 に 役 立 つ セキュアで 小 さく 扱 いや すいフリーライブラリです CSL は GitHub から 取 得 できます: https://github.com/coverity/coverity-security-library Maven Central からはこちらで す: http://mvnrepository.com/artifact/com.coverity.security このドキュメントでは Webページに 動 的 なデータを 挿 入 する 場 合 に 助 けとなるエスケーパーとサニタイザの 実 装 リファ レンスとしてCSLを 使 用 します また ここではJava Expression Language (EL) 表 記 を 利 用 します 通 常 のJava の 関 数 も 利 用 可 能 であることに 注 意 してください 何 が 使 えるかは GitHub 上 のドキュメントを 参 照 ください インストールと 使 用 方 法 GitHub 上 にドキュメントが 存 在 します また どのようにあなたのアプリケーションに CSL を 埋 め 込 むか そして Java JSPまたは EL からどのように 使 用 するかについては コードサンプルが 用 意 されています 対 処 方 法 通 常 のHTML 要 素 タグのボディ 内 部 でいくつかの 動 的 データを 表 示 したい これは 最 も 簡 単 なケースです 動 的 な 内 容 を 単 にHTMLエスケープします : 4
<div> Hello ${cov:htmlescape(name)}! </div> HTML 属 性 タグのための 属 性 値 を 動 的 に 構 築 したい まず バックティック(')ではなく ダブルまたはシングルクォートのいずれかで 属 性 が 括 られているか 確 認 してください すべての 属 性 について 確 認 する 必 要 があります クォートされていないHTML 属 性 を 使 用 してはいけません! 次 に 属 性 がURL JavaScript CSSまたは 他 の 埋 込 み 言 語 でないかをチェックします 属 性 のタイプのセクションを 探 して 確 認 します もしそれらのタイプでなければ HTMLエスケーパーを 使 用 します : <div data="${cov:htmlescape(content)}"> HTML 属 性 内 部 のJavaScript 文 字 列 JavaScriptを 含 む 属 性 にいくつかの 動 的 データを 挿 入 したい まず HTML 属 性 のセクションで 見 たように 属 性 が 適 切 にクォートされているかを 確 認 します 次 に 挿 入 しようとしているデータがJavaScript 文 字 列 であり その 文 字 列 がクォートされていることを 確 認 します もしあなたが 文 字 列 の 外 にデータを 挿 入 しようとしているのであれば CSLのドキュメントを 参 照 し 他 のJavaScript 文 字 列 エスケーパー(JavaScript string escaper)を 参 照 してください 次 に このコンテキストがネストされており 順 序 が 重 要 であるかを 識 別 します ブラウザがデコードする 順 序 が 以 下 で あ る 場 合 : 1. HTML 属 性 2. JavaScript 文 字 列 この 場 合 は JavaScript 文 字 列 エスケーパー(JavaScript string escaper)を 適 用 する 必 要 があり その 後 次 の ようにHTMLエスケーパーを 動 的 データに 使 用 します : <div onclick="jsfunc('${cov:htmlescape(cov:jsstringescape(content))}')"> クリックしてください </div> HTML 属 性 内 のフルURL 属 性 に リンクやiframeなどのURLとして 認 識 されるURLを 動 的 に 挿 入 したい まず HTML 属 性 のセクションでみたように 属 性 が 適 切 にクォートされているかを 確 認 します 次 に そのURLがflashファイルのためのobjectタグや script タグではなく あなたの 制 御 するサーバに 限 定 される 必 要 がないことを 確 認 します 5
ここまでくると これはネストコンテキストである 可 能 性 が 高 く XSSを 防 ぐためにフルURLを 正 しくエスケープでき ないでしょう しかし CSLは javascript: スキーマで 始 まるような 危 険 をともなうURLを 安 全 なURLに 変 換 する asurl と 呼 ばれるエスケーパー 関 数 を 備 えています asurl を 正 しく 使 用 するためには 構 成 されたフルURLに 対 して 適 用 する 必 要 があります これらを 踏 まえて 我 々の asurl 関 数 とHTMLエスケーパーを 適 用 すると 次 のようになります : <a href="${cov:htmlescape(cov:asurl(content))}"> クリックしてください </a> HTML 属 性 内 のCSS 文 字 列 font-family 属 性 や 属 性 内 に 指 定 したいCSS セレクターのようなCSS 文 字 列 の 操 作 をユーザに 制 御 させたい 願 わくば このあたりでコツがつかめてきているのではないでしょうか 以 下 のように 進 めます : 1. 属 性 をクォート " でくくる 2. CSS 文 字 列 をクォート でくくる 3. 属 性 内 のCSSのコンテキストの 重 なりを 識 別 する そしてCSS 文 字 列 エスケーパーを その 後 HTMLエスケーパーを 適 用 します : <a style="font-family:'${cov:htmlescape(cov:cssstringescape(content))}'"> クリックしてください </a> JavaScriptの 文 字 列 いくつかの 動 的 なデータを 文 字 列 に 書 き 込 むことでJavaScriptブロックに 渡 したい この 時 点 でネストされたコンテキストであるはずですが Webサイトを 複 数 のブラウザで 動 作 するように 作 成 したことが ある 方 であれば 誰 でも 知 っているように ブラウザは 変 化 に 富 んでいて 刺 激 的 で 正 すのが 困 難 な 状 況 を 好 むものなの で す! 幸 運 なことに 今 回 はそれほど 困 難 な 状 況 ではありませんし 我 々がすべきことは JavaScript 文 字 列 エスケーパーを 適 用 することです ここで 取 り 上 げるべき 注 意 点 としては あなたのJavaScript 文 字 列 エスケーパーが < を 一 般 的 な 文 字 と 同 様 にエスケープすることを 確 認 することです もしあなたがCSLを 使 用 しているのであれば 確 認 の 必 要 はあり ません 以 下 に 対 処 方 法 を 示 します : <script type="text/javascript"> var str = '${cov:jsstringescape(content)}'; // 文 字 列 'str' で 何 かを 行 う </script> 6
JavaScriptの 文 字 列 内 のHTML 動 的 データを あとでJavaScriptから 使 用 されるHTML 部 分 に 挿 入 したい 先 ほどの 例 と 同 様 に 動 的 データはJavaScript 文 字 列 の 一 部 となるため JavaScript 文 字 列 エスケーパーを 使 用 す る 必 要 があります しかし HTMLの 内 容 もエスケープする 必 要 も 出 てくるため HTMLエスケーパーも 使 用 します : <div id="formycontent"></div> <script> var foo = "<h1>${cov:jsstringescape(cov:htmlescape(content))}</h1>"; $("#formycontent").html(foo); </script> JavaScriptの 数 値 動 的 な 数 値 をscriptブロックに 書 き 込 むことでJavaScriptに 渡 したい 任 意 のデータをその 意 味 を 保 持 したまま 数 値 に 変 換 する 理 にかなった 方 法 は 存 在 しないため 我 々は 渡 された 文 字 列 が 数 値 であることを 確 認 し 返 す 関 数 を 提 供 しており そのデフォルト 値 として0を 返 すしくみをとっています このようなシ ナリオで 安 全 に 使 うには 次 のようになります : <script type="text/javascript"> var num = ${cov:asnumber(content)}; // 数 値 'num' を 使 用 して 何 かを 行 う </script> CSS 文 字 列 font-family ディレクティブのようなCSS 文 字 列 をユーザに 操 作 させたいか またはCSSセレクターを style タグ 内 で 指 定 したい scriptブロック 内 のJavaScript 文 字 列 のように これも 簡 単 なものの1つです 単 にあなたの 文 字 列 がクォートされて いるか 確 認 し そしてCSLのCSS 文 字 列 エスケーパー(CSS string escaper)を 使 用 します : <style> #foo[id ~= '${cov:cssstringescape(content)}'] { background-color: pink!important; } </style> 7
CSS 文 字 列 内 のURL style タグ 内 で バックグラウンドイメージなどのCSS URLをユーザにコントロールさせたい まず CSS URLがシングルクォートかダブルクォートによって 適 切 にクォートされているかを 確 認 してください 次 に CSS 文 字 列 エスケーパーを 使 用 する 必 要 があります 我 々は 単 なる 一 般 的 なJavaScriptのエスケーパーでは なく CSLのエスケーパーを 使 用 することを 強 くお 薦 めします これは CSSに 対 するセキュリティ 要 求 はJavaScript 文 字 列 とは 少 し 異 なるからです JavaScriptやHTML 内 のURLとは 異 なり 最 近 のCSSパーサーは javascript: や 他 の 特 殊 な 害 を 及 ぼす( 例 えば data: を 含 むHTMLの 内 容 など)URLは 翻 訳 しないため URLの 内 容 に 対 して 注 意 を 払 う 必 要 はありません : <style> #foo { background: url('${cov:cssstringescape(content)}'); } </style> CSSカラー バックグラウンドカラーのようなCSSカラーをユーザに 操 作 させたい CSSカラーは 文 字 列 内 に 指 定 されないため このコンテキストではエスケープすることができず いくつかの 種 類 の 検 証 やフィルタリングを 行 う 必 要 があります CSLではカラーの 指 定 として インジェクション 攻 撃 を 防 ぐ 16 進 数 またはテキ ストを 許 可 するフィルターを 提 供 しており 以 下 のように 使 用 することができます : <style> #foo { background-color: ${cov:ascsscolor(content)}; } </style> HTML 属 性 内 のURLフラグメント 動 的 なフラグメントをもつURLを 出 力 したい フ ラ グ メント( # 文 字 に 続 く)はURLのための 単 なるテキストです これに 付 随 するエスケープは 存 在 しないため 親 の コンテキストのエスケーパーを 使 用 する 必 要 があります これらは 時 にはURLエンコードされていますが ブラウザは それをデコードはしません 以 下 の 例 では HTML href 属 性 内 のURLの 出 力 と フラグメントがHTMLのためにエス ケープされていることが 確 認 されています : 8
<a href="/path/page?elmt=1#${cov:htmlescape(content)}"> リンク 名 </a> URLクエリ 文 字 とパス 要 素 パスもしくはクエリ 文 字 のいずれかに 動 的 データを 含 むURLを 出 力 したい その 他 のURLの 要 素 は 動 的 ではない URLのこれらの 要 素 :パス クエリ 文 字 の 要 素 名 クエリ 文 字 の 要 素 値 は URLエンコードにより 安 全 にできます CSL uriencode 関 数 がこれを 行 います すべてのURLエンコーダについてこれが 正 しいわけではないことに 注 意 してく ださい また CSLを 使 用 していない 場 合 は (HTMLエスケーパーとURLエンコーダを 使 用 した)ネストされた 方 法 が 必 要 になる 可 能 性 があります : <!-- 'name' と 'value' が 動 的 データであると 過 程 --> <a href="http://example.com/path/?${cov:uriencode(qselmtname)}= ${cov:uriencode(qselmtvalue)}"> クリックしてください </a> コベリティについて デベロップメントテストのリーダーである Coverity, Inc. は ソフトウェアの 障 害 から 自 社 ブランドと 最 終 損 益 を 守 ろう とする 企 業 に 信 頼 をおかれている 企 業 です 1,100を 超 えるCoverityの 顧 客 は 自 動 的 にソース コードをテストし 製 品 のクラッシュ 誤 作 動 セキュリティ 脆 弱 性 そして 致 命 的 な 障 害 につながるソフトウェアの 欠 陥 を 検 出 するCoverity デベロップメントテストの 製 品 パッケージを 採 用 しています コベリティは Foundation Capital およびBenchmark Capitalからの 資 金 提 供 を 受 けています コベリティジャパン 株 式 会 社 Website: http://www.coverity.com/html_ja/