はじめに
本記事では、CORS(オリジン間リソース共有)とその周辺知識について簡単に解説します。
参考になった方はいいねをお願いします。
オリジンとは
今回の主題でもあるCORSは、オリジン間リソース共有の略称です。
では、ここで言う オリジン とは何でしょう。
これは、URLのスキーム(プロトコル)、ホスト(ドメイン)、ポート番号の組み合わせのことを指します。
例えば、
https://www.google.com:443
- スキーム(プロトコル):https://
- ホスト(ドメイン):www.google.com
- ポート:443
がオリジンの一例です。
通常のURL(https)は、ポート番号(443)が省略されています。
(例)https://www.google.com ←ポート番号が省略されている
よく「ドメイン」 と混同しがちですが、プロトコルとポート番号も含まれるので注意しましょう。
練習
これは同一オリジン?
問1
http://www.hoge.com:80
http://www.hoge.com:8080
答え
同一ドメインではないポート番号が異なります。
問2
https://www.hoge.com/foo.html
https://www.hoge.com/bar.html
答え
同一ドメインファイル名が異なりますが、「プロトコル」「ホスト」「ポート」が等しいことから同一ドメイン内のリソースと判断できます。
問3
http://www.hoge.com
https://www.hoge.com
答え
同一ドメインではないプロトコルが異なります。
※省略されているポート番号も違います。
- http://www.hoge.com:80
- https://www.hoge.com:443
同一オリジンポリシー とは
異なるオリジンからのリソースへのアクセスを制限する仕組みのことです。
Javascriptなどを用いた開発を行う中で、このようなエラーを見たことはありませんか?
Access to fetch at 'https://hoge.com/' from origin 'https://foo.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled
これは、異なるオリジンに対してリソースの取得を要求しているために起こるエラーです。
例えば
https://foo.com/index.html
からhttps://hoge.com/api
に対してリソースを要求する場合は、異なるオリジン間のやり取りなので上記のエラーが起こる可能性があります。
逆に、
https://hoge.com/index.html
からhttps://hoge.com/api
に対してリソースを要求する場合は、同一オリジン内のやり取りなのでエラーは起きません。
このように、Webブラウザにはオリジン間の安全なリソースのやり取りを行うためのセキュリティ上の仕組みが備わっています。
これを、同一オリジンポリシー や 同一生成元ポリシー などと呼びます。
実際にエラーを出してみる
- このサイト上で開発者ツールを開く(F12キーなどで開けます)
- コンソールを開く
- 以下のJSコードを実行する。
await fetch('https://google.com')
以下のようなエラーが出れば成功です。
Access to fetch at 'https://google.com/' from origin 'https://qiita.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
正確には、JSの非同期通信の場合リクエスト自体は拒否されず、それに対するレスポンスが破棄されています。
【参考】同一オリジンポリシー - ウェブセキュリティ | MDN
await fetch('https://qiita.com')
このサイト上で上記を実行した場合はどうなるでしょうか?
制限の対象とならないもの
異なるオリジンへのアクセスでも、制限がかからない例外も存在します。
-
<script>
で読み込まれたJS -
<link>
で読み込まれたCSS -
<img>
で読み込まれた画像 -
<a>
で張られたリンク -
<video>
および<audio>
で再生されたメディア -
<object>
または<embed>
で埋め込まれた外部リソース -
@font-face
が適用されたフォント。 -
<iframe>
関連
など。
【参考】同一オリジンポリシー - ウェブセキュリティ | MDN
CORS(Cross-Origin Resource Sharing) とは
CORSは、同一オリジン間リソース共有の略であり、異なるオリジン間でリソースを共有するための仕様のことを指します。
上述の通り、XMLHttpRequestやfetchなどで外部のオリジンへリクエストをした場合、同一オリジンポリシーに引っかかりリソースへのアクセスが制限されます。
これに対し、CORSに準じて適切にアクセス範囲を設定することで、任意のオリジンからのアクセスを許可することができます。
具体的な実装としては、リクエストを受ける側(サーバー側)のレスポンスに、リソースの共有を許可するためのヘッダーを追加します。
実際に制限を解除してみる
https://hoge.com
からhttps://foo.com
へリクエストする場合、
hoge.com側(クライアントサイド)
リクエストヘッダにOrigin:
フィールドを追加し自身のオリジンを記載する。
Origin:https://hoge.com
foo.com側(サーバーサイド)
対応するレスポンスヘッダにAccess-Control-Allow-Origin
フィールドを追加し、アクセスを許可するオリジンを記載する。
Access-Control-Allow-Origin: https://hoge.com
Access-Control-Allow-Origin:*
のような記載もできるが、セキュリティ上よろしくないのでやめましょう。
Simple Requests(単純リクエスト)
CORSのリクエストは主に2つに分けられ、その中でもGET
,POST
,HEAD
などのシンプルなリクエストを単純リクエストなどと呼びます。
単純リクエストとは、以下の条件を全て満たしているものを指します。
-
メソッドが以下のいずれかである
GET
POST
HEAD
-
ヘッダーが以下の組み合わせで構成される(ユーザーエージェントによって自動的に設定されたヘッダー等を除く)
Accept
Accept-Language
Content-Language
Content-Type
Range
-
Content-Type ヘッダーの値がいずれかである
application/x-www-form-urlencoded
multipart/form-data
text/plain
【参考】オリジン間リソース共有 (CORS)|単純リクエスト
Preflighted Requests(プリフライトリクエスト)
事前確認を行ってからリクエストを送る方式をプリフライトリクエストと呼びます。
プリフライトリクエストでは、最初のリクエスト時にすぐリソースの要求をせず、OPTIONS
メソッドを用いてアクセス可否の確認を事前に行います。
要するに、メインのリクエストを送る前に、「こんなリクエストしてもいい?」という事前確認を行います。
【参考】オリジン間リソース共有 (CORS)|プリフライトリクエスト
なんでこんなめんどくさい処理があるの?
A. 許可されているオリジンからのアクセスを担保 & サーバーがCORSに対応しているかの確認
そもそも、単純リクエストとは元々Webブラウザで発行できていたクロスドメインのリクエストを指しています。(form
経由のPOSTなど)
CORSが生まれる前から存在していたものであり、改めて特別な対応をする必要が無いため事前確認無しのリクエストを受け付けています。
対して、プリフライトリクエストは主にCORSによって発行可能になったリクエストであり、XMLHttpRequestなどを経由しないとブラウザから発行できないものなので、サーバーによっては対応していない場合があります。
そのため、事前に確認用のリクエストを送ることで、CORSに対応しているかと自身のドメインが許可されているかを確認しています。
例)もしプリフライトリクエスト処理が無かったら...
サーバー側でAccess-Control-Allow-Origin
の設定をしていない状態(許可設定をしていない)で、DELETEリクエストが送られると...
- ブラウザ側(クライアント側)
→ CORSのエラーになり、処理が失敗しているように見える。 - サーバー側
→ リクエスト自体は届いているので、削除処理が実行されてしまう。
といった現象が起こりえます。
事前確認無しでリクエストを送っているので、クライアント側とサーバー側とで結果の乖離が生じてしまいますよね、
プリフライトリクエストでは、このような問題を防ぐため、メインのリクエストの前に確認用のやり取りを行います。
副作用のあるメソッドには注意!!
POST は副作用があるが単純リクエストです。
でも、Content-type:application/json
などが付くとプリフライトリクエストに...
終わりに
今回はCORSについて簡単に解説をしました。
誰もが必ずつまづく部分だと思いますが、この記事を通してざっくりと理解をしていただけたら嬉しいです。
参考になった方はいいねをお願いいたします。