社会人1年目の社員がCORSについて調べてみた

# はじめに
こんにちは！
[NTTコムウェア Advent Calendar 2021](https://qiita.com/advent-calendar/2021/nttcomware) の9日目です。

NTTコムウェアのNTT IT戦略事業本部に所属しています、1年目社員の原田です。
私は記事執筆年の2021年4月に入社し、現在の部署に配属され、これまでに２つのWebアプリケーション開発に携わってきました。現在は、プロジェクトの技術的なサポートや、技術ナレッジの蓄積およびそれらを展開するチームに所属しています。

今回の記事では、Webアプリケーション開発で引っかかりがちなCORS（Cross-OriginResource Sharing)についてまとめましたので、ご共有させていただきます。

# Webアプリケーション開発にて、こんなご経験はありませんか？
- 作成したWebサイトで他のWebサイトのデータ（地図やSNSの投稿など）を取得したいが、エラーが出て取得できない…
- 作成したWebサイトでAPIからのレスポンスデータを取得したいが、エラーが出て取得できない…

![データ取得ユースケース.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2334178/01c9de94-0865-c5c5-b201-e8834db24031.png)

# もしかしたらそれはCORSエラーかも
上記のようなエラーが出てデータを取得できないなどの事象が発生した場合、様々な原因が考えられますが、もしかしたらそれはCORSエラーかもしれません。
もし以下のようなエラーが開発者ツールに表示されており、他に思い当たる解決策がないといった場合は、CORSエラーを疑ってみましょう。

```
Access to XMLHttpRequest at 'http://~ from origin 'http://~' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
```

# なぜこのようなエラーが出てしまうのか
そもそもなぜこのようなエラーが出てしまうのでしょうか。
開発者からすればデータを取得したいだけなのによくわからないエラーが出てしまい、やや面倒臭さを覚えることでしょう...（少し前の私です）
ただ、そのような面倒臭いエラーですが、もちろん意味があってエラーが出ています。

それではなぜこのようなエラーが出てしまうかというと、Same-Origin Policy（同一生成元ポリシー）とよばれる、ブラウザが持つセキュリティ機能によって異なるオリジン間のリソース共有に制限がかかってしまっているからです。

ここでやや難しい単語二つが出てきました。

- オリジン
- Same-Origin Policy（同一生成元ポリシー）

まず以下で、この二つの単語の説明をしたいと思います。

# オリジンとは
オリジンは一般的に以下のように定義されています。

```
ウェブコンテンツにアクセスするために使用される、URLスキーム（プロトコル）、ホスト（ドメイン）、ポートによって定義されており、
URLスキーム、ホスト、ポートがすべて一致した場合のみ、二つのオブジェクトは同一オリジンである。
```

以上の説明だけではイメージが湧かないという方もいらっしゃると思うので、具体的に同一オリジンと異なるオリジンとしてどのようなものがあるか、例を見ていきましょう。

## 同一オリジンの例
|オリジン|説明|
|:---|:---|
|<ul><li>http\://example.com/qpp1/index.html</li><li>http\://example.com/app2/index.html</li></ul>|URLスキーム（http）およびホスト（example.com）が同じなので同一オリジン|
|<ul><li>http\://example.com:80</li><li>http\://example.com</li></ul>|サーバーは既定で80番ポートでHTTPコンテンツを配信するため、同一オリジン|

## 異なるオリジンの例
|オリジン|説明|
|:---|:---|
|<ul><li>http\://example.com/app1</li><li>https\://example.com/app2</li></ul>|URLスキームが異なる（httpとhttps）|
|<ul><li>http\://example.com</li><li>http\://ww.example.com</li><li>http\://nttapp.example.com</li></ul>|ホストが異なる（example.comとww.example.comとnttapp.example.com）|
|<ul><li>http\://example.com</li><li>http\://example.com:8080</li></ul>|ポートが異なる（規定の80番と8080番）|

以上のようなものが同一オリジンと異なるオリジンの違いです。
オリジンについて理解できたと思うため、次にSame-Origin Policy（同一生成元ポリシー）とは何か見ていきましょう。

# Same-Origin Policy（同一生成元ポリシー）とは
Same-Origin Policy（同一生成元ポリシー）とは、Webセキュリティの重要なポリシーの一つであり、異なるオリジン（以下、クロスオリジン）のリソース間の通信・アクセスを制限する、ブラウザが持つセキュリティ機能です。
このSame-Origin Policyによってクロスオリジン間のリソース共有に制限がかけられることで、CSRFのような脆弱性を防ぐことができます。
それでは具体的にSame-Origin Policyによって、どのようにCSRFのような脆弱性対策を行うことができるのでしょうか。

# CSRF（クロスサイトリクエストフォージェリ）とは
まずCSRFの説明です。
CSRFとは、「掲示板や問い合わせフォームなどを処理するWebアプリケーションが、本来拒否すべき他サイトからのリクエストを受信し処理してしまうこと」です。
もっと嚙み砕いて説明をすると、利用者がWebアプリケーションに①ログインした際、通常アプリケーションは②セッションIDを保持します。しかし、③ログアウトしなければセッションIDは保持されたままです。そして利用者は、④ログインしたまま悪意のあるサイトを閲覧し、⑤リンクやボタンなどをクリックすることで、正常なWebアプリケーションに対し、利用者のセッションIDが利用され、攻撃が仕掛けられます。

![クロスサイトリクエストフォージェリ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2334178/87c7d092-6765-2727-d94c-d6dc5a9be5f3.png)

以上のようにしてWebアプリケーションは攻撃を受けます。この攻撃をCSRFと呼びます。

# Same-Origin Policyのセキュリティ機能
CSRFのような脆弱性に対して、Same-Origin Policyによってどのようなセキュリティが担保されているのでしょうか。
Same-Origin Policyを担保するため、ブラウザはまずリクエストの内容を確認し、※単純リクエスト以外の場合、①プリフライトリクエストというリクエストを送信します。そして、②Webアプリケーション（バックエンド）からレスポンスヘッダーが返却され、そのレスポンスヘッダーの内容をブラウザが確認することで、ブラウザはWebアプリケーション（バックエンド）側がクロスオリジンからのアクセスが許可されているか判断します。許可されていない場合は、ブラウザ側でアクセスが拒否されるという仕様となっています。

※単純リクエストについては通信方式の説明となってしまうため、こちらの記事では説明を省きます。詳しくはこちらをご覧ください。
参照：<a href="https://developer.mozilla.org/ja/docs/Web/HTTP/CORS#%E5%8D%98%E7%B4%94%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88">単純リクエストについて</a>

まとめると、以下の図のように、悪意あるクロスオリジンからアクセスをされた際、正常なWebアプリケーションへの攻撃を阻止するため、Same-Origin Policyは存在しています。

![プリフライトリクエスト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2334178/59962f43-9d6f-eb0d-9e40-2a62a16ecc65.png)

もし許可されていないオリジンからアクセスされた際は、Same-Origin Policyによってブラウザ側でアクセスを拒否され、それがCORSエラーとして開発者ツールに表示されます。以上がCORSエラーが起こる仕組みです。

しかし、Webアプリケーション開発において、クロスオリジン間のリソース共有をしたい場面は多々あると思います。
そのような場面で、クロスオリジン間のリソース共有を許可する仕組みである「CORS」で設定を行う必要があります。

# CORS（Cross-OriginResource Sharing)とは
それではこの記事の本題であるCORSの説明です。
CORSとは、あるオリジンで動作しているウェブアプリケーションに、異なるオリジンにある選択されたリソースへのアクセス権を与えるようブラウザーに指示するための仕組みです。
クロスオリジン間でリソースの共有を行いたいときは、CORSの設定を行う必要があり、設定を行わなければアクセス権がなく、CORSエラーが発生してしまいます。

以上で、CORSエラーがなぜ発生するのかについて理解できたと思います。そして最後に、CORSエラーを回避するための、CORSの設定方法を説明します。

# CORSの設定およびCORSエラーの回避方法
CORSの設定やCORSエラーに対応したい際は、バックエンドからのレスポンスヘッダに、以下のコードを入れることで対応します。

```
Access-Control-Allow-Origin:　https://www.example.com　//許可するオリジン
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT 　//許可するリクエストメソッド
```

ちなみにフロントエンドからのリクエストヘッダに以下のコードを追加する必要がありますが、ブラウザの機能で自動的にリクエストヘッダに追加してくれるため、設定は不要です。

```
 Origin: https://api.example.com 　//アクセスを許可するオリジン 
 Access-Control-Request-Method: POST 　//許可するメソッド 
 Access-Control-Request-Headers: X-PINGOTHER, Content-Type　 //許可するリクエストヘッダ
```

### 注意点
レスポンスヘッダのAccess-Control-Allow-Originに「＊」と記述すると、すべてのオリジンからのアクセスを許可することになります。しかし、前述のCSRFのような脆弱性を生んでしまうため、この記述方法は商用のプロダクトだとあまり対応方法としてよろしくありません。
できるだけ、具体的に許可するオリジンをヘッダーに記述しましょう。

# おわりに
以上がCORSの説明および対応方法です。
この記事が、CORSで躓いた方やCORSについて知らなかった人の助けになれば幸いです。

今後も、[NTTコムウェア Advent Calendar 2021](https://qiita.com/advent-calendar/2021/nttcomware) をお楽しみください！