同一生成元ポリシー(Same Origin Policy: SOP)は、外部APIを利用するWebサービスにとっては避けては通れない話です。最近はSPAの台頭により自前のAPIをclientから叩かせるケースが増加していると思いますが、その場合でも意識しなければならないことがあります。
目的と概要
何のためにSOPが必要なのでしょうか。
簡単なWebページでは、ブラウザはサーバーからデータ(HTML ファイル)を取得しそれをレンダリングします。一度取得したらそれっきりです。何か追加で定期的にデータを取りに行ったりすることはありません。ボタンを押せば画面遷移はしますが、それは新しくページを取りに行っているだけです。
しかし、最近はJavaScriptなどを利用して追加でデータを取得するような仕組みを採用するWebページが増えています。SPAはその最たる例です。このような目的で行われるアクセスは、もとのページとサーバーが同じ(同一オリジン)であり、ブラウザもこれについては特に制限しません。
では、異なるサーバーへのアクセス(クロスオリジン(後述))だったらどうでしょう。ここで問題なのは、アクセスするのはサーバーからではなくブラウザからだということです。(「Webサイトが…を取得する」という表現はそのWebサイトのサーバーがデータを取得するという意味で用いられることも在るかもしれませんが、この場合のWebサイトはまさにWebサイト(のJavaScriptなど)がデータを取得しに行くという意味です)
つまり、Twitterにアクセスするのならユーザーが普段見るようなログイン済みのTwitterの画面を取得できてしまうことです。(サーバーからのアクセスなら問題のない話です。ユーザーがIDとパスワードを渡さなければよいからです。逆に、ブラウザが取得するなら問題ないだろうと考える人もいるかも知れません。しかし、取得したデータをJavaScriptを用いて元のサーバーに送ることは可能なはずです。)
ブラウザとしてはこれを認めるわけには行きませんので、ブロックします。
SOPはJavaScriptなどから異なるオリジンへの干渉をブラウザレベルで防ぐことにより、ユーザにとってのセキュリティを担保するための仕組みなのです。
オリジンの定義
以下の3つがすべて一致する組を同一とみなします。
- ホスト(FQDN)
- スキーム(httpとかhttpsも区別される)
- ポート
ですから、以下のいかなる組み合わせも異なるオリジンとみなされます。
- http://support.cosnomi.com/
- http://**blog.**cosnomi.com/
- https://support.cosnomi.com/
- http://support.cosnomi.com**:8080**/
以下のいかなる組み合わせも同一オリジンです。
- https://support.cosnomi.com/
- https://support.cosnomi.com/maintenance/
- https://support.cosnomi.com:80/
繰り返しになりますが、アクセス元とアクセス先が同一オリジンならブラウザはブロックしません。同一オリジンでないなら、ブラウザはそのアクセスに制限をかけることになります。その制限について見ていきましょう。
何が出来て何が出来ないか
異なるオリジンから来たデータについてブラウザはどのような制限を行うのでしょうか。
- XMLHttpRequest
- iframeやWindow
- Canvas
他にも様々な制限がありますが、この3つ、特にXMLHttpRequestを重点的に説明します。
XMLHttpRequest(XHR)はサーバーから非同期に(受信済みのwebからさらにリクエストできる)データを取得するための仕組みで、Ajaxなどで重要な技術です。前述のようなJavaScriptによるアクセスというのはXHRを利用しています。同一オリジンに対しては、XHRを用いてデータを取得できますが、異なるオリジンへのアクセス(クロスオリジン)は許可された場合を除き、ブラウザによってブロックされます。これによって、前述のような不正なデータ取得を防止します。
また、iframeやwindowに対する操作の制限も行われます。要するに埋め込まれたコンテンツの中身は、埋め込んでいるWebサイトからは取得できないということです。
Canvasもiframeなどと同様に一部の操作が制限されます。
回避策
とはいえ、これでは不便な場合もあります。
例えば、 https\://example.com/
から送信されたJavaScript中で https\://api.example.com/news
にアクセスしてデータを取ってきて表示したい場合を考えます。異なるオリジンですので、アクセスは制限されてしまいます。これが、自前のAPIを利用する場合でもSOPに引っかかるというケースです。いくらサーバー側でどうにかしようと思ってもSOPはブラウザ側でのブロックですので通常はどうすることもできません。
しかし、これは非常に不便です。そもそも問題なのは、アクセス元のスクリプトが悪意を持って、アクセス先にあるクライアントに関する情報を取得しようとしていることです。つまり、アクセス先のサイトがアクセス元を信頼しているということがブラウザに伝われば、ブラウザはこのような制限を掛ける必要はないわけです。この信頼関係をブラウザに伝える方法として、CORS という技術があります。詳しくは、CORSについて説明した記事をご覧ください。
まとめ
- 同一生成元ポリシーはクロスオリジンでのアクセスを制限する考え方
- オリジンは「ホスト」「スキーム」「ポート」の組
- JavaScriptをりようしたXMLHttpRequestも制限の対象
- 回避するにはCORSなどを用いる