守る資産、脆弱性、脅威の3つが揃ったとき、そこにセキュリティリスクが発生します。 ここでは、脆弱性を減らすことで、セキュリティリスクの低減を図る方法を示します。 具体的にここでは、JavaScript の サニタイジング を行うことで XSS(クロスサイトスクリプティング) を防ぐ方法をみていきます。
目次
警戒すべき出力地点
以下の場所で サニタイズ が行われているか チェック します。 適切に行われているかどうかは、 コードレビュー で チェック します。
- スクリプトを呼び出せる部分
- iframe.src
- script.src
- img.src
- スクリプトを実行できる部分(DOM要素のイベント)
- img.onerror 等
- element.style の expression ( 例:<div style="z:expression(...)"></div> )
- スクリプトを直接挿入できる部分
- element.innerHTML
- document.write
- 文字列をスクリプトと解釈する部分
- setTimeout
- setInterval
- eval
サニタイズ の 対象文字列
JavaScript で 特別な意味を持つ文字列 = エスケープすべき文字列 は以下の通りです。
- < → <
- > → >
- " → "
- ' → '
- & → &
サニタイズ の 方法
上記 無害化すべき対象文字列 を エスケープする関数、およびその関数の利用例を以下に載せます。
サニタイズ関数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /** * @namespace */ var sanitize = sanitaize || {}; /** * HTML, JavaScipt で特別な意味を持つ文字列を無害な文字列へ変換します。 * @public * @param {string} str 変換元の文字列 * @return {string} 無害化された文字列 */ sanitaize.encode = function (str) { return str.replace(/&/g, '&' ).replace(/</g, '<' ).replace(/>/g, '>' ).replace(/ "/g, '"').replace(/'/g, '''); }; /** * エスケープされた文字から元の文字列を復元します。 * @public * @param {string} str 無害化された文字列 * @return {string} 無害化前の元文字列 */ sanitaize.decode = function (str) { return str.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '" ').replace(/'/g, ' \ '' ).replace(/&/g, '&' ); }; |
サニタイズ関数 の 使い方
1 2 3 4 5 6 7 | var message; // 有害なコード message = '<script>window.alert("foo-bar");</script>' ; // エスケープして表示 document.getElementById( 'message' ).innerHTML = sanitize.encode(message); |
ちなみに、上記 "使い方" の場合、わざわざ サニタイズ せず次のような コード の方が、処理速度と安全性の観点から適切だと思います。
1 | document.getElementById( 'message' ).appendChild(document.createTextNode(message)); |
何が問題なのか
「任意のスクリプトが実行できる」脆弱性が問題です。 詳細は XSS(クロスサイトスクリプト)で検索いただければ、それだけで沢山の記事がでてきます。 が、ここでは「任意のスクリプトが実行できるとどのようなことが起こるか」の例を1つ挙げてみます。
脆弱性が引き起こす問題の事例
- サービスA は「任意のスクリプトが実行できる」脆弱性がある
- サービスA の脆弱性を利用して「アクセスしてきた人のログインID、パスワードを 別サーバーBへ転送するスクリプト」を埋め込む
- ...しばらく待つ(=サービスA 利用者の情報がサーバーBに集まるのを待つ)
- サーバーB に集まった ユーザーID、パスワードを利用して、サービスA を利用する(=なりすまし)
この例は、かなり単純化した上、ことごとくダメなサービスを例としています。 こんなことにならないように、注意したいものです…
今回、以下のサイトを参考にしました。
- (new Hatena).blog() - escapeHTML の実装 3 パターン (ベンチマーク付き)
- Thousand Years - [JavaScript]window.toStaticHTML())
- MSDN - toStaticHTML method
- HTML の安全性を高める: toStaticHTML の詳細 (JavaScript と HTML を使った Windows ストア アプリ)
- IPA セキュア・プログラミング講座 - 第8章 マッシュアップ #3 クライアント側コードに起因するスクリプト注入対策
最後に… このブログに興味を持っていただけた方は、 ぜひ 「Facebookページ に いいね!」または 「Twitter の フォロー」 お願いします!!