JSONPはセキュアでないのか?

JSONPという強力なAjaxアプローチが認知されだしたとたん、JSONPってセキュリティ的にやばいんじゃないの、という話がそこらここらで聞かれる。注意して使った方がいいよー、とか、どうなっても知らないよー、とか。

JSONPで軽量Webサービスインフラを、と提唱してる身としては、もしほんとにリスクが大きいのであれば早めに何とか手を打ちたいわけです。


とりあえずこちらで思いつく限り、JSONPのリスクっぽいものをあげてみる。



まず最初に思いつくのが「これってクロスサイトスクリプティングじゃないの?」という話。なんか文字だけ捉えるとそれっぽいね。確かにクロスサイトだし、スクリプトだし。

クロスサイトスクリプティング(XSS)とは

動的にWebページを生成するシステムのセキュリティ上の不備を意図的に利用し、サイト間を横断して悪意のあるスクリプトを混入させること。また、それを許す脆弱性のこと。

名称の由来
- 本来のサイトと異なるサイトからスクリプトが混入されること
- 混入したスクリプトが本来のサイト以外のサイトにリクエストを送ることができること

とある。なんとなく名称の由来だけ見るとJSONPも抵触してそうに見える。

しかし多分この記述は、言外に「本来のサイトが『意図していない』サイトからスクリプトが混入される」という意味を含んでいると思われる。JSONPの場合、あらかじめ呼び出すべきスクリプトの所在をサイトの作成者はあらかじめ認識しているはず。つまり事前に呼び出し先のサービスに対して信頼関係がある状態であり、信頼関係のないまったく赤の他人のスクリプトを混入させられるわけじゃない。その点が大きく異なる。



次に、これはJSONPの特徴だけど、パラメータ入力にJavaScriptのコールバック関数名を指定できる件について。ここに悪意あるコードが紛れ込むのではないか、という懸念。

多分「注意して使った方がいいよ」という意見はこのあたりに起因してるのだろうか。

このコールバック関数名の指定に、外部からの入力をそのまま使う(URLクエリ文字列から取ってきたりとか)のは、やはり避けた方がいいだろう。

しかし大体の場合はコールバック関数名を外部入力値に起因する値にするような真似はしないことが多いのではないか?関数名はあらかじめコードに固定で埋め込みしていたり、動的にしてもdojoみたなJSONPライブラリを使ってたらその部分は予測可能な生成値だ。

もちろんJSONPサービスを利用するクライアントは通常不特定多数であり、そのクライアントの実装をある特定の前提で考えるのもなんなので、JSONPサービスのサーバ開発者は念のためコールバック関数名のチェックをしておいたほうがいいかもしれない。実際Yahooは関数名に使用できる文字を制限してる。

ただ、サーバ側がその対策をしなかったからといってそのJSONPサービスがセキュリティ的にだめかというと、多少疑問が残る。

JSONPという言葉を初めて出した人の記事の中では、JSONPとはJSONデータの前に任意のJavaScriptコードを付け加えることができるというアイディアだった(ゆえにJSON with Paddingとなっている)。その意味では実は変数代入のJSONデータロードもJSONPの一種として実現できる。ただ、なんとなく現在は関数コールバックの形式のものをJSONPと考えることが多いように思う。

まあ何がいいたいかというと、そもそものJSONPは、そのリクエストパラメータに渡したものをクライアントでそのままコードとして実行することを前提としていたわけだ。それは結局eval()を直に実行するようなものであって、それに対しての内容のチェックはむしろクライアントの責任だ、というスタンスなのだ。

JSONPの定義が微妙にずれてきていることもあり、結局どっちの責任になるのかの分解点ははっきりしない。経験的にはスクリプト読み込み完了のタイミングで事前指定のコードが実行できたとしてもあまりメリットはないので、サービス側が使える文字を固定しておいても不都合はないだろうとも思う。ただ、JSONPに指定する関数名を外部入力に依存してしまうような値にしてしまうのは、結局クライアント側の責任のような気がする。たとえ任意のコード実行は防がれたとしても、意図されない定義済みの関数でコールバックが実行されたらやっぱりまずいわけだし。

いずれにせよ、これは使用上の注意喚起の話であって、別にJSONP自体に不可避なセキュリティリスクがある、というわけではないように思われる。



最後に「JSONPサービス側が(故意にしろ過失にしろ)悪意のあるコードを返してきたら、クライアントは太刀打ちできない」というところ。

これは確かにそのとおり。なんとなくこれが一番JSONPを危うく見せている気がする。

通常のWebサービスでは、データを受け取った後に、その内容を妥当性検査することができる。なので期待されない内容のデータが送られてきたとしても、最悪エラーで処理が止まるだけだ。

JSONPでは、それが妥当性検査も何もなく、いきなりその呼び出し元のサイトのコンテキストで評価されてしまう。渡ってくるデータはJavaScriptコードそのものだから、そのサイトにできることすべて、動きを完全に掌握できるといっても過言ではない。

こうなると「危険だからJSONPなんて使うな!」と言ってしまいたいのもわかる。でも現在のネットを見てると、別にこれはJSONPに限った話ではなく、よくあるパターンになってしまっているのがわかる。

例えばアフィリエイト経済圏の立役者である Google AdSense。内部を見てみると、Googleのサイトにおいてあるスクリプトを読み込むようになっている。Google AdSenseを使ってるサイトには、ページ中にこのスクリプト読み込みのコードが貼り付けられている。

もしこのJavaScriptコードが悪意のあるものに改変されたら、数々のサイトが多大な影響を受けるだろう。

この点はまったくJSONPと同じだ。つまり、この理由でJSONPを否定するなら、本来こういったサービスも否定されなければならないはずである。

ただ、そのリスクを知ってか知らずか、この手のJavaScriptをインクルードさせるサービスは既に大多数に受け入れられている。AdSense然り、Google Maps然り。この状況を覆すのはきっと至難だろう。

実践的には、もしそのJavaScriptコードを提供するサイトが、利用するサイトにとって十分信頼に足るのであれば、そのまま使うのは構わないのではないだろうか。

きっと現在そういったリスクへの理解が足りないのは確かだろうが、危険だから絶対に使ってはだめ、という類のものでもない気がする。ぼんやりと何となく危ないよ、というよりは、むしろこういうレベルでリスクがあるんだ、ということをある程度明らかにしておくべきなんじゃないかな。そしてその上で有効な使い道を考えていくのが正しい気がする。

(追記 07/01/13)
JSONPのpadding 部分に任意の文字列が設定できることについての脆弱性の考察はこちらの記事をご覧ください。上記の文章では指摘されている視点(インクルードされる側のリスク)が抜けています。
http://labs.cybozu.co.jp/blog/kazuho/archives/2007/01/jsonp-security.php