CORSを理解する

## CORSとは
CORSとは、追加のHTTPヘッダーを使用して、あるオリジンで動作しているウェブアプリケーションに、異なるオリジンに選択されたリソースへのアクセス権を与えるようブラウザに指示するための仕組みのことで、**オリジン間リソース共有**と呼ばれています。

## オリジンって何？
オリジンとは、ウェブコンテンツにアクセスするために使われるURLの **スキーム（プロトコル）**、**ホスト（ドメイン）**、**ポート**によって定義されているものです。

例えば、``http://example.jp``の場合

* スキーム : http
* ホスト : example.jp
* ポート : 80 (httpのデフォルト値)

## 同一オリジンポリシーとは
ではなぜCORSは必要なのか、それを理解するためには**同一オリジンポリシー**の理解が必要です。

**同一オリジンポリシー**とは、重要なセキュリティーの仕組みであり、あるオリジンによって読み込まれた文書やスクリプトが、他のオリジンにあるリソースにアクセスできる方法を制限するものです。セキュリティにおける重要な仕組みであり、悪意ある行動を起こしかねないリソースの分離を目的としています。

この仕組みによって、webページを生成したオリジン以外へのHTTPリクエストはできなくなります。

#### もし同一オリジンポリシーがなかったらどうなるか

1. 悪意ある人から届いたメールにリンクが貼ってある
1. そのリンクをクリックすることで遷移し、JSがダウンロードされ実行される
1. 攻撃対象のサイトへリクエストが送られる
1. そのサイトの個人情報などが悪意あるサイトへ送られる（個人情報漏洩)

#### 同一オリジンポリシーがある場合

攻撃対象であるサイトへリクエストを行った時点でブロックされるので情報漏洩を避けることができます。

同一オリジンポリシーのおかげで、別のオリジンであるサイトへのリクエストは通らなくなります。

おかげで悪意ある攻撃から情報などを守ることができるようになったのですが、異なるオリジンへのアクセスはできなくなりました。

しかし、``AJAX``の普及によりAPI呼び出しなどで異なるオリジンへのアクセスが必要になりました。
そこで発案されたのが**CORS**です。

## CORSの基本特性
#### 提供する機能
* クロスオリジンのリソースアクセスを提供
* オリジン単位のアクセス制御を提供

### 後方互換性
* 従来動作しているサイトもCORSの元でも動作する
* 従来のCORSを使用しないサイトの性能劣化は無視できる程小さい
* 従来セキュリティ上問題のないサイトが``CORSの元でも安全に動作する``

### HTTPヘッダを用いたアクセス制御
* リクエストヘッダ : ``Origin, Access-Control-Request-XXXX``
* レスポンスヘッダ : ``Access-Control-Allow-XXXX``

### リクエスト
* 単純リクエストについては無条件でリクエストを送信して、レスポンスヘッダにより、JavaScriptがリソースを受け取れるか否かを判断
* 単純ではないリクエストに関しては、``プリフライトリクエスト``により、リクエスト送信の許可を得る

## CORS対応ブラウザの挙動
### 許可がない場合
1. ``http://example.jp``へアクセスしJSファイルをダウンロード
1. JSからGETでAPIを提供する``http://api.example.net``へリクエストを行う
1. リクエストを行うにはリクエストヘッダにOriginを付与したリクエストを行う
（``Origin: http://example.jp``）
1. APIサーバーがJSONを作り、レスポンスを返す
1. レスポンスヘッダに``Access-Control-Allow-Origin``がないためレスポンスを受け取ることはできない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2851087/d70c62d0-af72-fa83-73af-7e87734efb66.png)

### 許可がある場合
1. ``http://example.jp``へアクセスしJSファイルをダウンロード
1. JSからGETでAPIを提供する``http://api.example.net``へリクエストを行う
1. リクエストを行うにはリクエストヘッダにOriginを付与したリクエストを行う
（``Origin: http://example.jp``）
1. APIサーバーがJSONを作り、レスポンスを返す
1. レスポンスヘッダに``Access-Control-Allow-Origin``を付与する
1. リクエスト元のオリジン``http://example.jp``のCORSが許可されたため、リクエスト元はリソースを受け取ることができる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2851087/1051781c-022f-018c-26a5-7f7f30e69a8f.png)

## プリフライトリクエストとは
これまでのものは、**単純リクエスト**と呼ばれています。
### 単純リクエストの要件
#### メソッドは以下のいずれか
* GET
* POST
* HEAD

#### 設定できるリクエストヘッダは以下のいずれか
* Accept
* Accept-Language
* Content-Language
* Content-Type(以下の要件を満たすもの)

#### Content-Typeヘッダは以下のいずれか
* application/x-www-form-urlencoded
* multipart/form-data
* text/plain

これらの条件を満たすものが**単純リクエスト**です。

### 単純リクエストではない場合はプリフライトリクエスト
1. PUTリクエストを送信
1. PUTメソッドは単純リクエストではないため、事前にプリフライトリクエストが送られる
1. この時、HTTPメソッドは**OPTIONS**が使用される
1. **Access-Control-Request-Method: PUT**を付与して、PUTでのアクセスをして良いかお伺いをたてる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2851087/ca6e4993-7f8f-2104-4c5d-b790e5ef423e.png)



#### 正規のルートの場合
1. プリフライトリクエストをサーバーが受け取る
1. ``Access-Control-Allow-Method: PUT``
1. ``Access-Control-Allow-Origin: http://api.example.jp``
1. これらをレスポンスヘッダに付与する
1. 許可が得られたためPUTリクエストが送られる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2851087/26d2d4fc-6703-35ff-89e8-ac1bdb0339c6.png)
プリフライトリクエストにはリスクがないのかという疑問がありますが、
プリフライトリクエストは、クッキーやパラメータが飛ばないのでリスクは極めて小さくなっています。

## まとめ
* 単純リクエストの場合は無条件でHTTPリクエストが送信される
* 単純リクエストではない場合、先にプリフライトリクエストでお伺いをたててから本番のリクエストを送信して良いか決定する
* プリフライトリクエスト自体のリスクは極めて小さい

基本的にCORSに関してはフレームワークがいい感じにやってくれるのですが、原理を知って正しく使用していきたいと思います。