この記事は Ateam Lifestyle Inc. Advent Calendar 2021 1日目の記事です。
概要
API開発しているとCORSエラーに遭遇した経験はありませんか?
CORSとはそもそもなんなのか?今回はエラー回避法ではなく、その成り立ちなども含めて、「CORSとはそもそもなんなのか?」を書いていきます。
なぜ成り立ちを知る必要があるのか?
仕事だと現象のエラー回避だけして、それが生まれた背景や成り立ちまで知る余裕がありません。そのままにしていては、自分の中でも応用が効かないと思ったのでこのテーマにしました。
仕事としてはその方が助かりますが、せっかくのアドカレの機会なので、セキュリティの話から始めて、そもそもCORSとはなんのためにあるのか?ついて書こうと思います。
そもそもCORSってなんて読むの?
あらゆるYoutubeを見て発音を聞きました。読み方はコルス
またはコース
でいいと思います。
セキュリティについて
JavaScriptはWebブラウザ上で手軽に実行できます。それ自体は素晴らしいと思う一方で、危険なこともあります。Webブラウザ上でJavaScriptが実行できるということは、誰が作ったかもわからない、どう動くかもわからないプログラムが実行され得ます。
Webブラウザ上には、自分が登録しているネット銀行やクレカのWebサービスやSNSなど、重要な情報が保存されている方も少なくないでしょう。よく考えると結構危なっかしい代物ですよね。にもかかわらず、JavaScriptが原因で致命的な情報漏洩が起きた例は0ではないにしろ頻繁に聞くなんてことはないですよね?
その理由は、いくつかのセキュリティ対策が働いているからでした。今回はそれに関して調べた内容を書いていきます。
サンドボックス
JavaScriptが安心安全に利用できる理由は、サンドボックス(sandbox)で動作するかららしいです。
サンドボックス上で動作していると、アクセスできるファイルや実行できるプログラムが制限されてしまいます。
その代わりに、その制限下では自由な利用が許可されています。
JavaScriptのサンドボックスによる代表的な制約の一つに同一オリジンポリシー (same-origin policy) があります。
同一オリジンポリシー (same-origin policy)について
まずオリジンとは、オリジン == スキーム + ホスト + ドメイン + ポート番号
のことです。
同一オリジンポリシーは、今見ているWebサイトからオリジンの異なるリソースにアクセスする際に適応されるポリシーです。これがあることで、JavaScriptは、基本的にはオリジンの異なる内容を読み取ることができません。
画像引用👇
CORSについて
同一オリジンポリシーは、Webをユーザーとして利用する分には素晴らしいのですが、Web開発者としては困ることもあります。
例えば、JavaScriptで関数fetch()
を実行しようとしても、同一オリジンからしか呼び出せなかったら焦りますよね。
とはいえ実際の開発の現場では、異なるオリジン間でAPI通信を行うことができます。
ここでやっとCORSの登場です。
**CORS(Cross-origin resource sharing)**という技術を利用すると、異なるオリジン間でのリソース取得が可能になります。
CORSを用いてデータの読み込みが許可されたことを確認する
CORSはHTTPヘッダを使用して、サーバからブラウザへ異なるオリジン間(Cross-origin)でリクエストを可能にする仕組みです。
ここでは仮にhttps://xyz.jp
からhttps://xxx.com
に対してfetch("https://xxx.com")
して、以下のエラーが出たとします。
Access to fetch at 'https://xxx.com' from origin 'https://xyz.jp' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
エラー文を読むと、「リクエストされたリソースに 'Access-Control-Allow-Origin'ヘッダーがありません」とあります。
さて、コレを解決したい場合、どこでどんな設定をすればいいでしょうか?
CORSの判定するのは、サーバー側でもJavaScriptでもなく、常にブラウザです。そして、CORSを成功させるには、ブラウザでもJavaScriptでもなく、サーバを修正する必要があります。
CORSはサーバのレスポンスヘッダーによりブラウザーが通信を制御する仕様なので、ちゃんと検証したい場合は、簡易的でもサーバを立てるほうがいいでしょう。
そこらへんの解説は世の中にたくさんある記事に任せるとして、ここではCORSの単純な設定である以下を試してみます。
curlコマンドで、リクエストのオリジンヘッダにオリジンを設定して送信し、サーバはそのオリジンを確認して、もし通信を許可するならレスポンスのaccess-control-allow-origin
ヘッダに設定したレスポンスを返す方法で確認してみます(実際のケース、コレで解決できるとは限りません)。
JSONPlaceholderに対してcurlコマンドで接続してみましょう。
JSONPlaceholderとはRESTで実装されたダミーデータを返してくれるAPIサーバーです。テストなどに使える便利なサービスです。
curl -v telnet://jsonplaceholder.typicode.com:80
* Trying 2606:4700:3035::ac43:83aa:80...
* Connected to jsonplaceholder.typicode.com (2606:4700:3035::ac43:83aa) port 80 (#0)
以下を入力
リクエストのオリジンヘッダにオリジンを設定(http://localhost:8080
)
GET /posts HTTP/1.1
Host: jsonplaceholder.typicode.com
Origin: http://localhost:8080
サーバからのレスポンス内容
HTTP/1.1 200 OK
Date: Tue, 23 Nov 2021 08:04:37 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
x-powered-by: Express
x-ratelimit-limit: 1000
x-ratelimit-remaining: 999
x-ratelimit-reset: 1636702086
access-control-allow-origin: http://localhost:8080
vary: Origin, Accept-Encoding
access-control-allow-credentials: true
cache-control: max-age=43200
// 続く
レスポンスには
access-control-allow-origin: http://localhost:8080
が含まれています。ブラウザがこのレスポンスを受け取ると、このサーバとは一定期間クロスオリジンの通信が許可されることを意味します。
まとめ
-
- CORSとは、ブラウザのポリシーで異なるオリジンへのアクセスを許可するもの
-
- CORSエラーの回避方法は、アクセスを受ける(サーバ)側に対してレスポンスヘッダに
Access-Control-Allow-Origin
ヘッダを付与する
- CORSエラーの回避方法は、アクセスを受ける(サーバ)側に対してレスポンスヘッダに
さいごに
本記事では、CORSエラーの回避方法を網羅できていません。
以下の記事を見ると、さまざまなセキュリティ対策を重ねてきた歴史があり、まだまだ知るべきことはありそうですが本記事ではここまでとします。
CORSの判定に関しては、リクエストした条件や、サーバのレスポンスによって通信の可否が分かれます。その大まかなフローに関しては、下記の書籍(p407)が役立ちました。
今回勉強した内容を基礎にして、今後の勉強に役立てます。
参考記事
アウトプット100本ノック実施中
お時間がある時にでもどうぞ