eval() is "evil"? chrome特権コード内でのevalすることの危険性について

http://icanhascheezburger.files.wordpress.com/2008/03/funny-pictures-good-and-evil-kittens.jpg

Growl/GNTPアドオンの件を通して、JSONをパースするにあたって eval() がいかに危険であるか認識したので、エントリにまとめてみる。

chrome特権を持つコードでのwindow.eval()の危険性について

JSON文字列からオブジェクトを得る方法として、多くの例としてeval()でJSON文字列を評価するという方法が使用される。この方法はお手軽である反面、セキュリティ的なリスクが存在する。なぜなら、evalで評価される文字列はそのままJavascriptの命令として実行されるから。

もしJSON文字列に悪意のあるコードを紛れ込ませることが出来た場合に、攻撃者は用意に訪問者への攻撃を仕掛けることができてしまう。これが、コンテンツ内のJavascriptでの話であってもcookieが盗まれるとか、そこからセッションハイジャックされたり、といったように攻撃者側にある程度の知識があれば結構怖いことができてしまう。(具体的な攻撃については実はよく知らない、自分の想像が入っている)

今回の場合、さらに悪い点として、chrome特権を持つコンテキストでevalを行っていること。これによりevalされたコードもchrome特権で実行されることになり、ローカルのリソースに容易にアクセスできることとなり、最悪、重要なファイルが盗まれたり、消されたりという攻撃が比較的簡単に行えてしまう状況になっていた。

それを実証したものとして、サイト側のコンテンツのjavascriptコードからそれを参照しているローカルPCのメモ帳を起動するということもできたりする。それを試せるのが以下のサイト(※最新のGNTP/Growlアドオンでは対処済みのため、起動は行えなくなってる)
http://www.paw.hi-ho.ne.jp/makochi/growl.html

じゃあJSONをどう扱ったらいいのか?

以下の方法があるらしい

  • JSON.jsm の JSON.fromString メソッドを使う (Firefox3.0.x系の場合)
  • Firefox3.5以降では JSONオブジェクトがネイティブサポートされるのでそのメソッド JSON.parse を使用する。
  • 上記以外のJSONを扱うjavascriptライブラリを使用する。
  • Components.utils.evalInSandbox を使って評価する

上記の中から、とりわけ現状のFirefoxバージョンにおける対応として最善とおもわれるJSON.jsmを使用する方法について調べてみた。

JSON.jsmについて

JavascriptJSONフォーマットを簡単/安全に扱うためのUtilityが格納されているJavascript module。Firefox3.0系で使用可能。

JSON.jsmの使い方
//インポートする
 Component.utils.import("resource://gre/modules/JSON.jsm");
//オブジェクトからJSON文字列を得る
 var jsonString = JSON.toString( hoge );
//JSON文字列からオブジェクトを得る
 var parsedObj  = JSON.fromString( " { hoge : 'fuga' } " );
JSON.jsmの利点

JSON.fromString()がwindow.eval()より優れている点は、パース(評価)される文字列の内容が特定の型であるかどうかをチェックしており、関数など危険な文字列が混入している場合にチェックにかかり、不用意に実行されてしまうのを防ぐことが出来る。

JSON.jsmの注意点

時期バージョンであるFirefox3.5からJSONオブジェクトをネイティブにサポートするようになる予定であり、その場合メソッド名が以下のように変更になる。

用途 FF3.0.x系(JSON.jsm) FF3.5以降(ネイティブJSON)
文字列をパースしてオブジェクト取得 JSON.fromString() JSON.parse()
JavascriptオブジェクトをJSON文字列を生成 JSON.toString() JSON.stringify()

この変更ととともに、JSON.jsmはバンドルされなくなる。

以下のようなコードを入れておけば、FF3.0.xとFF3.5の両方で動作させることができるはず。

if (typeof JSON == "undefined"){ 
    Components.utils.import("resource://gre/modules/JSON.jsm");
    JSON.parse = JSON.fromString;
    JSON.stringfy = JSON.toString;
}

id:teramakoGrowl/GNTP修正パッチから見つけたもの。

ToDo

id:Constellationのコメントにあった以下の点についてもあとで調べてエントリ書くつもり。

  • addEventListenerメソッドの第4引数について
  • GMContextへのメソッドの追加

追記:2009.6.18 evalInSandboxについて

id:teramakoからコメントがあったように、上記以外に Components.utils.evalInSandbox メソッドを使用する方法もある。
evalInSandboxについてはまだちゃんと調べきれてないので簡単に補足してみると、
window.evalより安全なevalという感じ。ただしこれも使い方を間違えるとセキュリティリスクに晒される場合があるので、evalInSandboxをつかってるから全面的に大丈夫ってわけでもないので、その点には注意が必要っぽい。
あとでちゃんと調べたら、エントリとしてとりあげるかも知れない。

id:teramako++