CORSとは
CORSとは、追加のHTTPヘッダーを使用して、あるオリジンで動作しているウェブアプリケーションに、異なるオリジンに選択されたリソースへのアクセス権を与えるようブラウザに指示するための仕組みのことで、オリジン間リソース共有と呼ばれています。
オリジンって何?
オリジンとは、ウェブコンテンツにアクセスするために使われるURLの スキーム(プロトコル)、ホスト(ドメイン)、ポートによって定義されているものです。
例えば、http://example.jpの場合
- スキーム : http
- ホスト : example.jp
- ポート : 80 (httpのデフォルト値)
同一オリジンポリシーとは
ではなぜCORSは必要なのか、それを理解するためには同一オリジンポリシーの理解が必要です。
同一オリジンポリシーとは、重要なセキュリティーの仕組みであり、あるオリジンによって読み込まれた文書やスクリプトが、他のオリジンにあるリソースにアクセスできる方法を制限するものです。セキュリティにおける重要な仕組みであり、悪意ある行動を起こしかねないリソースの分離を目的としています。
この仕組みによって、webページを生成したオリジン以外へのHTTPリクエストはできなくなります。
もし同一オリジンポリシーがなかったらどうなるか
- 悪意ある人から届いたメールにリンクが貼ってある
- そのリンクをクリックすることで遷移し、JSがダウンロードされ実行される
- 攻撃対象のサイトへリクエストが送られる
- そのサイトの個人情報などが悪意あるサイトへ送られる(個人情報漏洩)
同一オリジンポリシーがある場合
攻撃対象であるサイトへリクエストを行った時点でブロックされるので情報漏洩を避けることができます。
同一オリジンポリシーのおかげで、別のオリジンであるサイトへのリクエストは通らなくなります。
おかげで悪意ある攻撃から情報などを守ることができるようになったのですが、異なるオリジンへのアクセスはできなくなりました。
しかし、AJAXの普及によりAPI呼び出しなどで異なるオリジンへのアクセスが必要になりました。
そこで発案されたのがCORSです。
CORSの基本特性
提供する機能
- クロスオリジンのリソースアクセスを提供
- オリジン単位のアクセス制御を提供
後方互換性
- 従来動作しているサイトもCORSの元でも動作する
- 従来のCORSを使用しないサイトの性能劣化は無視できる程小さい
- 従来セキュリティ上問題のないサイトが
CORSの元でも安全に動作する
HTTPヘッダを用いたアクセス制御
- リクエストヘッダ :
Origin, Access-Control-Request-XXXX - レスポンスヘッダ :
Access-Control-Allow-XXXX
リクエスト
- 単純リクエストについては無条件でリクエストを送信して、レスポンスヘッダにより、JavaScriptがリソースを受け取れるか否かを判断
- 単純ではないリクエストに関しては、
プリフライトリクエストにより、リクエスト送信の許可を得る
CORS対応ブラウザの挙動
許可がない場合
-
http://example.jpへアクセスしJSファイルをダウンロード - JSからGETでAPIを提供する
http://api.example.netへリクエストを行う - リクエストを行うにはリクエストヘッダにOriginを付与したリクエストを行う
(Origin: http://example.jp) - APIサーバーがJSONを作り、レスポンスを返す
- レスポンスヘッダに
Access-Control-Allow-Originがないためレスポンスを受け取ることはできない
許可がある場合
-
http://example.jpへアクセスしJSファイルをダウンロード - JSからGETでAPIを提供する
http://api.example.netへリクエストを行う - リクエストを行うにはリクエストヘッダにOriginを付与したリクエストを行う
(Origin: http://example.jp) - APIサーバーがJSONを作り、レスポンスを返す
- レスポンスヘッダに
Access-Control-Allow-Originを付与する - リクエスト元のオリジン
http://example.jpのCORSが許可されたため、リクエスト元はリソースを受け取ることができる
プリフライトリクエストとは
これまでのものは、単純リクエストと呼ばれています。
単純リクエストの要件
メソッドは以下のいずれか
- GET
- POST
- HEAD
設定できるリクエストヘッダは以下のいずれか
- Accept
- Accept-Language
- Content-Language
- Content-Type(以下の要件を満たすもの)
Content-Typeヘッダは以下のいずれか
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
これらの条件を満たすものが単純リクエストです。
単純リクエストではない場合はプリフライトリクエスト
- PUTリクエストを送信
- PUTメソッドは単純リクエストではないため、事前にプリフライトリクエストが送られる
- この時、HTTPメソッドはOPTIONSが使用される
-
Access-Control-Request-Method: PUTを付与して、PUTでのアクセスをして良いかお伺いをたてる
正規のルートの場合
- プリフライトリクエストをサーバーが受け取る
Access-Control-Allow-Method: PUTAccess-Control-Allow-Origin: http://api.example.jp- これらをレスポンスヘッダに付与する
- 許可が得られたためPUTリクエストが送られる

プリフライトリクエストにはリスクがないのかという疑問がありますが、
プリフライトリクエストは、クッキーやパラメータが飛ばないのでリスクは極めて小さくなっています。
まとめ
- 単純リクエストの場合は無条件でHTTPリクエストが送信される
- 単純リクエストではない場合、先にプリフライトリクエストでお伺いをたててから本番のリクエストを送信して良いか決定する
- プリフライトリクエスト自体のリスクは極めて小さい
基本的にCORSに関してはフレームワークがいい感じにやってくれるのですが、原理を知って正しく使用していきたいと思います。