JSONPの動作原理
前回はAjaxに存在するセキュリティモデルであるSame-Originポリシーを紹介し、そのSame-Originポリシーを迂回する方法とセキュリティについて見てきました。また、回避する方法の1つめとしてリバースProxyを用いた方法を紹介しました。リバースProxyを用いた方法ではセキュリティ的な問題点もありましたが、そもそもProxyサーバを用意しなければならないため、この方法は手軽に使うことはできませんでした。
そこで考え出されたのがJSONP(JavaScript Object Notation with Padding)という方法です。
それではまず簡単にJSONPについて説明します。
Ajaxで使われるXMLHttpRequestオブジェクトには前回説明したとおりSame-Originポリシーがありクロスドメインアクセスはできません。一方、SCRIPTタグを用いると、ドメインの異なるサーバに置いているスクリプトファイルを読み込むことができます。このスクリプトファイルの中にデータを入れておくことで、ドメインの異なるサーバからデータを取得できるという仕組みです。
では簡単なプログラムを用いて説明していきます。次のようなファイルを用意し、関数を定義しておきます。この関数は、のちほどSCRIPTタグでJSONPデータを読み込んだ時点で実行されますので、コールバック関数と呼ばれます。
JSONPのデータは以下のようになります。このデータをSCRIPTタグで読み込んだ時点でコールバック関数が実行されることになります。SCRIPTタグにはSame-Originポリシーがありませんので、異なるドメインのサーバにJSONPデータを置くこともできます。
JSONPとは、このようにSCRIPTタグが他のサーバからもデータを取得できる(Same-Originポリシーがない)という特徴を生かし、クロスドメインでのアクセスを実現する方法です。
また、リバースProxyを使った方法ではリバースProxyサーバに認証情報を送信しなければならないという問題がありましたが、JSONPを使う方法ではその問題が解決されます。もしデータ提供サーバが認証を必要とするサービスを提供していた場合、データ提供サーバへCookieなどの認証情報が送られます。これはブラウザが自動的に送信します。つまりJSONPを使った場合には、本来認証情報を受け取るべきサーバであるデータ提供サーバにだけ認証情報を送信することができます。
JSONPのセキュリティ
さて、JSONPを使用してクロスドメインアクセスを実現する方法には、セキュリティ上どのような問題点が存在するかを考えてみます。
JSONPの特徴は、どのサーバからもSCRIPTタグを用いて、データ提供サーバのデータにアクセスできるという点です。これはSCRIPTタグの利点でもありますが、欠点でもあります。では欠点が悪用される例を見てみましょう。
例として、JSONPのデータhttp://mail.example.com/json.datに機密情報を含める場合を考えます。
機密情報にアクセスする権限があるかのチェックをCookieで行っていたとします。この場合、第三者(攻撃者)がhttp://mail.example.com/json.datにアクセスしたとしても、正当な認証情報(Cookie)を持っていないために内容を読み取ることはできません。
しかしこの攻撃者は以下のようなHTMLファイルを設置したサイトに被害者を誘導します。(1)
誘導された被害者は不正なスクリプトを読み込まされます。(2)
このスクリプトにより被害者のブラウザはhttp://mail.example.com/json.datへデータを取得しにいきます。このとき被害者がログイン状態であれば認証情報(Cookie)を含んだリクエストが自動的に送信されるためデータを取得することができます。(3)
その後コールバック関数が実行され、取得したデータは攻撃者に送信されます。(4)
このように、SCRIPTタグからJSONPを呼び出す場合にはSame-Originポリシーが存在しないため、他のドメインのサーバからも呼び出すことができます。そのため、攻撃者は被害者に気づかれないように、ドメインの異なるサイトへリクエストを送信させることができます。その結果、レスポンスに機密情報が含まれていた場合には、攻撃者が機密情報を取得することができます。
なお、この攻撃は広い意味で言えばCSRF(Cross Site Request Forgery)と呼ばれています。攻撃者がレスポンスを取得できるという点が一般のCSRFとは異なっており、情報漏えいが発生するため被害も大きくなります。
この脆弱性はGmailで発見され有名になりました。JSONPでコンタクトリスト(メールアドレスのリスト)が提供されていたため、コンタクトリストの内容が漏えいするという事態になっていました。
機密情報を含んだJSONP
それでは対策について考えてみます。一番の対策はJSNOPに機密情報を含めないことです。そもそもJSONPは、不特定多数の人にサービスを利用してもらいたいために使用するためのものです。そのため、不特定多数の人から読まれることを前提とする、つまり機密情報は扱わず公開情報のみを扱うのが本来の姿です。
しかし、どうしても機密情報を含めてクロスドメインで利用したい場合は、以下のようにすることで、機密情報を含めて提供することができます。
・クエリストリングに認証情報を含める
クエリストリングに推測不可能な文字列を含めておき、JSONPファイルを読み出す場合にその値を用いて認証を行うようにします。すると攻撃者はJSONPファイルのURLがわからなくなるため、図2のように被害者に気づかれないようにリクエストを発生させる攻撃を防ぐことができます。
・Refererヘッダをチェックする
呼び出し元のサイトを制限しておくことで、図2のように攻撃者のサイトから呼び出されることを防ぎます。しかし、Refererヘッダはパーソナルファイアウォールなどのセキュリティソフトウェアで、送信しないように設定されていることがあります。その場合はRefererヘッダをチェックする方法は使えません(セキュリティソフトウェアを導入しているユーザが、サイトを利用できなくなります)。
JSONPを読み込む場合のセキュリティ
JSONPでデータを提供する場合のセキュリティについて説明しましたが、JSONPのデータを読み込む場合にも注意することがあります。JSONPのデータを読み込む場合には、SCRIPTタグを用いて外部のスクリプトを読み込むことになりますが、これには注意が必要です。外部スクリプトには不正なコードが含まれている可能性があるためです。
もし外部のスクリプトに不正なコードが含まれていた場合には、認証情報の盗難などの被害が発生する可能性があります。さらに、外部スクリプトの呼び出しはユーザがWebサイトにアクセスするたびに発生するため、たとえ現在は不正なコードを挿入されていなかったとしても、今後いつ挿入されるかわからない状態になります。また、いったん不正なコードを挿入し、攻撃が完了した後に元の状態に戻してしまえば、攻撃が行われていた痕跡も残りにくくなります。
このようなことが起こりうるということを知ってか知らずにか、今日ではSCRIPTタグを用いて外部リソースを読み込むということは、一般的になりつつあります。アフリエイト広告やアクセス解析サービスなどが代表例ですが、これらは企業のWebサイトから個人のブログにいたるまで、広く使われています。このようなサービスを使う際には、今回紹介したような危険が伴うということを認識しておいてください。攻撃を防ぐためには、信頼できるサイトからのみJSONPを読み込むようにしてください。
次回はFlashを使ってクロスドメインアクセスを行う方法を紹介したいと思います。