「CORS」について~SOPも添えて~

スタッフブログ

みなさん、あけましておめでとうございます。ゆるりと過ごした年末年始も終わり、また忙しい日々が始まってしまいましたね。
どうも、お休み気分が抜けず寝ても寝ても眠たい谷口です。

新たなゲームに手を出し続け、プレイ時間が足りず疲弊しており、更には推しのイベントがそろそろ来るのではないかと、公式からの告知が来るたびに緊張が走る日々を送っているため今とてもシンドいです。やるならはやくやってくれ切実に。

さて今回は、仕事をしている中で意味が分からなすぎて理解するのに苦労した「CORS」に関して、記事にしたいと思います。インプットを結構頑張ったのでアウトプット出来るかな?と思ってますけど、相変わらず拙い文章なので優しい目で見て下さい。

本題の前に、SOPについて

本当はすぐにでも本題に行きたいところなんですが、CORSについて知る前に知っておかなければならないことがあります。それが「SOP(Same-Origin Policy)」と呼ばれるものです。
日本語訳は「同一生成元ポリシー」で、同じオリジン内でしかデータの送受信をさせない機能のことです。

オリジンとはスキーム、ホスト、ポート番号の組み合わせのことです。といってもそれぞれの単語が分からない人もいると思うので分かりやすくするためにURLの説明をしたいと思います。

私たちがよく見るURLはスキーム、ホスト、ポート番号、パスの組み合わせで出来ています。ポート番号は省略することも出来るので実際見る機会はあまりないかもしれません。
もっと分かりやすくするために例として自社のサイトで説明します。

自社サイトのトップページのURLはhttps://komari.co.jpです。

このURLはスキーム(https)、ホスト(komari.co.jp)の組み合わせです。

このトップページから「service/ウェブ制作」のページに飛んでみます。
するとURLは、https://komari.co.jp/service/となります。

このURLはスキーム(https)、ホスト(komari.co.jp)、パス(/service/)の組み合わせです。

どちらのURLも省略されているためポート番号はありませんが、その場合httpsには443というポート番号が使用されています。
これまでの説明を以下の表にまとめてみます。

URL スキーム ホスト ポート番号 パス
https://komari.co.jp https komari.co.jp 443 /
https://komari.co.jp/service/ https komari.co.jp 443 /service/

表で見ると分かるように、2つのURLはスキーム、ホスト、ポート番号が同じなので、このURLは同一オリジンということになります。

このように同一オリジンならデータの送受信が出来るように、異なるオリジンならばデータの送受信が出来ないように制限をかけるのがSOPです。

正直仕事をしている最中に「こんな面倒くさいものを誰が作ったんだ」と真剣に思っていましたが、勿論SOPが導入されたのにも理由があります。
この制限は自由度の高いJavaScriptに制限をかけるために導入されました。JavaScriptは基本的に何でも自由に出来てしまいます。自由にできてしまうが故に不正アクセスなどのサイバー攻撃を行うことも出来ます。そのためJavaScriptと同時期にSOPをブラウザに導入しました。

SOPの考えとして、同一オリジンならスクリプト自身のサービスなので通信させ、異なるオリジンなら他者のサービスなので通信させない、といった動きになります。

とまあ以上のように色々いいこともあるのですが、やっぱり別オリジンからデータの送受信がしたい時もあります。ということでそこで出てきたのが、ようやく登場「CORS(Cross-Origin Resource Sharing)」です。

やっと本題、CORSとは

「CORS(Cross-Origin Resource Sharing)」は日本語訳で「オリジン間リソース共有」のことです。読み方は「シーオーアールエス」か「コルス」です。

これは異なるオリジン同士のアクセスを許可、つまりSOPの制限を例外的になくしてくれる仕組みのことです。

異なるオリジンと通信する場合、本来のリクエストの前にブラウザがプリフライトリクエストというリクエストを自動的に発行し、その後サーバ側のレスポンスヘッダの「Access-Control-Allow-Origin」でサーバ側が許可しているオリジンを確認します。

そして「Access-Control-Allow-Origin」に記述してあるものとスクリプトのオリジンが同じ場合は、ブラウザは本来のHTTPリクエストを送信し、異なるオリジン同士のアクセスを行います。逆に「Access-Control-Allow-Origin」に記述したあるものと違う場合は、ブラウザは通信を許可できないというエラーを返します。

実行例を紹介します。

サーバサイドではHTTPのレスポンスヘッダに以下のどちらかを設定します。

Access-Control-Allow-Origin: https://example.com // 特定のオリジンからのアクセスを許可
Access-Control-Allow-Origin: * // 全てのオリジンからのアクセスを許可

これを設定することで、CORSが承認されます。

今回はクライアントサイドで行う例としてfetch APIで紹介します。
クライアントサイドではfetch APIならmodecorsに設定します。

fetch("http://example.com", {
    mode: "cors",
});

これを実行すると、プリフライトリクエストが発行され、上記で説明したような流れで通信します。

これらを行えば、異なるオリジン間でも通信を行うことが出来ます。しかし誰かが提供しているWebサービスを使っていて、サーバサイドのレスポンスヘッダの設定が上記のどちらでもなかったり、自分でレスポンスヘッダをいじることが出来ない場合はこの機能は使えません。
その場合はCORSの設定とかではなく別の方法を考えましょう。因みに私は別の方法を考えることになりました。辛い。

終わりに

ここまで調べて最終的に出来ないということに気づくのは最高に辛いものですね。しかし、新しいことを沢山知ることが出来ましたし、色々調べるのは楽しかったのでまあよしとします。
でも辛いものは辛いし疲れた!!!ということでこのへんで終わろうと思います。今回は投稿するのが遅くなったのでまたすぐにブログをあげることになると思います。ネタ作りに必死。
ではまた。