# オリジン間リソース共有(CORS)
「CORS エラーを解消するには」「CORS のリクエストの種類」みたいな記事はたくさん見つかりますが、そもそもの目的や登場人物の役割みたいなものを整理しているものが少なかったので確認した範囲でまとめたい。
# 参考
オリジン間リソース共有 (CORS) - HTTP | MDN
CORS(Cross-Origin Resource Sharing)について整理してみた
# CORS の目的
前提として Web には同一オリジンポリシーというセキュリティのポリシーがある。
同一オリジンポリシーとは、あるオリジンから読み込まれた文書やスクリプトについて、そのリソースから他のオリジンのリソースにアクセスできないように制限するものです。同一オリジンポリシーはウェブのセキュリティにおける重要な仕組みであり、悪意ある行動を起こしかねないリソースの分離を目的としています。
SOP はクロスオリジンでのアクセス時にブラウザがエラーを発生させることで実現されています。つまりブラウザの実装に依存しています。
CORS は、SOP に基づいたオリジン間でのリソースのアクセス制限の中で、指定されたオリジンからのリクエストを許可するための仕組みの仕様。 つまりクロスオリジンのリソースへのアクセスは制限されているという前提の元、特定にリクエストについてホワイトリスト的にアクセスを許可するのが CORS の目的になる。
# CORS の仕組み
CORS でのアクセスの許可/拒否の判定には HTTP のリクエスト/レスポンスヘッダを利用し、ユーザエージェントとサーバが連携してアクセス制限を実現する。
# 登場人物と役割
登場人物 | 役割 |
---|---|
サーバ | コンテンツの提供者。コンテンツにアクセスできるオリジンやリクエストの詳細を定義する。その情報はレスポンスヘッダとしてユーザエージェントに渡す。 |
ユーザエージェント(ブラウザ) | CORS の仕様に従ってクロスオリジンのリソースへのリクエストを行う。レスポンスヘッダを見てアクセスが可能であるかを判断し、必要であれば制限を実施する |
クライアントアプリケーション | クロスオリジンへのアクセスしたいアプリケーション。リクエストを定義する。リクエストが許可された場合だけレスポンスを受け取れる。 |
# 簡単な流れ
- ユーザエージェント がオリジン aaa.example.com のサイトへアクセス
- aaa.example.com の サーバ からコンテンツが ユーザエージェント に返される
- ブラウザでコンテンツがレンダリングされ、 クライアントアプリケーション が実行される
- クライアントアプリケーション から、aaa.example.com とはクロスオリジンである bbb.example.com のコンテンツへアクセスを要求
- メソッドや資格情報の付与などをクライアントプリケーションが設定
- ユーザエージェント が bbb.example.com へアクセス
- CORS の仕様にもとづいて Origin ヘッダなどを付与
- 必要に応じてプリフライトリクエストを実施
- bbb.example.com の サーバ からコンテンツが ユーザエージェント に返される
- リクエストヘッダに応じて、CORS 関連のレスポンスヘッダが付与
- ユーザエージェント がクライアントへレスポンスのデータを渡す(許可時) or ユーザエージェント がレスポンスを破棄してエラーを起こす(制限時)
- ユーザエージェント がレスポンスヘッダに応じて許可/制限を判断する
- プリフライトリクエストで許可されたときはこの後に実際のリクエストが行われる。
WARNING
実際にクロスオリジンのリソースへアクセス時に、エラーが発生しているのはユーザエージェント(ブラウザ)である。サーバはアクセスの可否を判断するための情報をレスポンスヘッダに付与するだけであり、HTTP エラーを返すといった動作はしない。
プリフライトリクエストではない場合、ユーザエージェントにおいて CORS エラーが発生した場合には、レスポンスがクライアントアプリケーションに渡されないだけであって、クロスオリジンでの API 呼び出し自体は行われている点は注意が必要。
# プリフライトリクエストの意義
それぞれチェックする対象が違う点に関係する。
リクエストの種類 | チェック対象 |
---|---|
プリフライトリクエスト | permission to make the request |
シンプルリクエスト | permission to read |
"permission to make the request" のチェックはリクエストの発行前に確認する必要があるため、プリフライトリクエストを事前に送ることでクロスオリジンでのリソースのアクセスが許可されているかをユーザエージェントが確認する。
具体的には、サーバに変更を加える API ( PUT
や DELETE
や一部を除く POST
)のとき、実際の API コールが行われる前にチェックしたいのでプリフライトリクエストが送信される。
一方で、GET
や HEAD
についてはサーバに変更を与えない API のため、実際の API コールによって "permission to read" を確認する。
一部の Content-Type を持つ POST
についてもプリフライトリクエストが必要ないのは、CORS が提起される前から <form>
で発生する POST
ではクロスオリジンのリソースへのアクセスが許可されていたから。
<form>
で発生するタイプの POST
については CORS でもプリフライトリクエストを必要としない。
To allow sharing responses cross-origin and allow for more versatile fetches than possible with HTML’s form element, the CORS protocol exists.