📝

CORSで起こっていること

2024/05/26に公開

はじめに

開発中のWebアプリケーションでCORS対応が必要になったものの、CORS?なにそれおいしいの?状態だったので調べてまとめてみます。

CORSとは

CORSとは、異なるオリジンのリソースへのアクセス権を与える仕組み

CORSはCross-Origin Resoure Sharingの略で、日本語だと「オリジン間リソース共有」のこと。
ブラウザの仕組みで、ブラウザからHTTPリクエストでサーバーのリソースにアクセスする際に使用されている。※オリジンとは?

雑な説明だと、WebサイトAからWebサイトBのリソースにアクセスできるようにしましょうという仕組み。これがあるおかげで、閲覧しているサイトから別サイトへのリンクが叩けたり、こんな風に別サイトの中身を表示できたりする↓
https://developer.mozilla.org/ja/docs/Web/HTTP/CORS

起きていること

クライアントとサーバーとのやり取りの中でブラウザは、

  • リクエストヘッダ―のOrigin:リクエストが発生したオリジン
  • レスポンスヘッダーのAccess-Controll-Allow-Origin:アクセスを許可するオリジン

を比べて、2つが同一であればレスポンスの中身を取得する。

例)リクエストヘッダ―
Origin: http://localhost:8080

例)レスポンスヘッダー
Access-Controll-Allow-Origin: http://localhost:8080

どちらかが欠けていたり、2つのオリジンが異なる場合、ブラウザはレスポンスを破棄しエラーを返す。
ここで注意したいのは、CORSはブラウザ側の仕様であり、リクエスト時のヘッダー情報とレスポンス時のヘッダー情報を比較している点である。つまりアクセス許可がなかった場合は、リクエストが送られないのではなく、ブラウザがリクエストを送りレスポンスを一度受け取ってから破棄している。

CORSを実装するには

リクエストヘッダーはブラウザが自動でつけてくれる

リクエストでは、ヘッダーにOriginという「どこのオリジン(ホスト)でリクエストが発生したか?」という情報が記述される。
これはブラウザからリクエストを送る際に自動で追加されるため、実装は不要。

レスポンスヘッダーはサーバーで設定が必要

レスポンスでは、ヘッダー情報としてアクセスを許可するオリジンは何かを送信する必要がある。
これは自動で追加されないので、サーバー側で実装が必要。
以下のように記述する。

例)http://localhost:8080からのアクセスを許可する
Access-Controll-Allow-Origin: http://localhost:8080

例)すべてのオリジンからのアクセスを許可する
Access-Controll-Allow-Origin: *

プリフライトリクエスト

CORSの一部で、実際のリクエストを送る前にリクエストを送っても問題ないか?を確認するためのリクエストを送信するというもの。

『CORSとは』で前述したとおり、CORSではアクセス許可があるかを確かめるために、ブラウザは一度リクエストを送る必要がある。
そのため、GET、POST、HEADでリソースを閲覧するだけなら問題ないが、DELETEメゾットなどのリクエストでリソースに対して処理を行う場合は問題となってしまう。プリフライトリクエストはこれをどうにかするためのリクエストのこと。

プリフライトリクエストの流れは以下の通り。

  1. ブラウザがOPTIONSメゾットで以下のヘッダーを含んだリクエストを送信
  • Access-Control-Request-Method:送りたいリクエストのメゾット
  • Origin:リクエストが発生したオリジン
  1. サーバーは以下のヘッダーを含んだレスポンスを返す
  • Access-Control-Allow-Origin:アクセスを許可するオリジン
  • Access-Control-Allow-Methods:通信を許可するメゾット
  • Access-Control-Allow-Headers:通信を許可するヘッダーの種類
  1. ブラウザはリクエストとレスポンスを比較してアクセス許可があるかチェック
  2. アクセス許可があれば実際のリクエストを送り、アクセス許可がなければ送信しない

 
実装する際は、上記の流れ2の

  • Access-Control-Allow-Methods:通信を許可するメゾット
  • Access-Control-Allow-Origin:アクセスを許可するオリジン
  • Access-Control-Allow-Headers:通信を許可するヘッダーの種類

これらをOPTHONメゾットのレスポンスヘッダーに実装する必要がある。

単純リクエストになる(プリフライトリクエストにならない)条件

許可されているメソッドのうちのいずれかであること。
GET
HEAD
POST
ユーザーエージェントによって自動的に設定されたヘッダー (たとえば Connection、 User-Agent、 または Fetch 仕様書で禁止ヘッダー名として定義されているヘッダー)を除いて、手動で設定できるヘッダーは、 Fetch 仕様書で CORS セーフリストリクエストヘッダーとして定義されている以下のヘッダーだけです。
Accept
Accept-Language
Content-Language
Content-Type (但し、下記の追加の要件に注意してください)
Range (単純範囲ヘッダー値、例えば bytes=256- や bytes=127-255 の場合)
 
Content-Type ヘッダーで指定できるメディア種別に許されるタイプ/サブタイプの組み合わせは、以下のもののみです。
application/x-www-form-urlencoded
multipart/form-data
text/plain

https://developer.mozilla.org/ja/docs/Web/HTTP/CORS#単純リクエスト

この条件を見ると、POSTメゾットのリクエストでもContent-Type: application/jsonだとフライトリクエストになるっぽい。CORSについて調べているとき、ここではまったという記事もいくつかあったので注意ポイント。

COREはなぜ必要になった?

  • ブラウザは元々、同一オリジンのリクエストのみを受け付ける仕様だった
  • AJAXの普及により、異なるオリジンのAPIを呼び出す必要が生まれた

ということでCORSは生まれたらしい。

まとめ

CORSとは、異なるオリジンのリソースへアクセスするための仕組み。
ブラウザ側の仕組みで、サーバーはヘッダーに情報を追加することでアクセス許可を出せる。

ヘッダーには以下の情報を追加する。

  • リクエストメゾットがGET,POST,HEADの場合:単純リクエスト
    Access-Controll-Allow-Origin

  • リクエストメゾットがその他のメゾット(PUT,DELETEなど)の場合:プリフライトリクエスト
    Access-Controll-Allow-Origin
    Access-Controll-Allow-Methods

補足

オリジンとは、URLのスキーム(プロトコル)・ホスト(ドメイン)・ポート番号の組み合わせ

例)
URL:https://localhost/home/my_page:8080

スキーム:https://
ホスト:localhost
ポート番号:8080

参考文献

https://developer.mozilla.org/ja/docs/Web/HTTP/CORS

Discussion