LoginSignup
34
29

スキマ時間で理解するCORS

Last updated at Posted at 2023-10-06

はじめに

本記事では、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ブラウザにはオリジン間の安全なリソースのやり取りを行うためのセキュリティ上の仕組みが備わっています。
これを、同一オリジンポリシー同一生成元ポリシー などと呼びます。


実際にエラーを出してみる

  1. このサイト上で開発者ツールを開く(F12キーなどで開けます)
  2. コンソールを開く
  3. 以下の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:*のような記載もできるが、セキュリティ上よろしくないのでやめましょう。

【参考】オリジン間リソース共有 (CORS)

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について簡単に解説をしました。
誰もが必ずつまづく部分だと思いますが、この記事を通してざっくりと理解をしていただけたら嬉しいです。

参考になった方はいいねをお願いいたします。

34
29
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
29