クロスオリジンリソース共有（CORS）とは？+AWS ソリューションアーキテクトに求められる観点

## はじめに
AWSの資格を全て取得しました。勉強している時に、良く出てくるけど、詳しくはよくわかっていなかった **クロスオリジンリソース共有（CORS）** について、調べてみました。まず、AWSの公式サイトに記載されている内容をおさらいし、アーキテクトに求められる観点を提示し、仕組みについて理解を深めていきたいと思います。

## クロスオリジンリソース共有が重要なのはなぜですか?
インターネット技術がまだ新しかった時代では、**クロスサイトリクエストフォージェリ (CSRF) の問題**がありました。これらの問題では、被害者のブラウザから別のアプリケーションに偽のクライアントリクエストが送信されていました。

たとえば、被害者が銀行のアプリケーションにログインしたとします。その後、だまされて新しいブラウザタブに外部ウェブサイトを読み込まされたとします。そして、その外部ウェブサイトは被害者のクッキー認証情報を使用して、被害者に**なりすまして**データを銀行のアプリケーションに中継するのです。結果として、権限のないユーザーが銀行のアプリケーションに**意図せずアクセスする**ようになったのです。

__このような CSRF 問題を防ぐことを目的として、すべてのブラウザが同一オリジンポリシーを実装するようになりました。__

https://aws.amazon.com/jp/what-is/cross-origin-resource-sharing/

## AWS ソリューションアーキテクトに求められる観点
あなたは、ユーザー用の登録フォームを含む、認証されていない静的 Web サイト (www.example.com) を運営しています。登録フォームが送信されると、ウェブサイトは Amazon API Gateway API エンドポイントを呼び出し、AWS Lambda 関数を呼び出してペイロードを処理し、ペイロードを外部 API 呼び出しに転送します。

__テストを行うと、クロスオリジンリソース共有 (CORS) エラーが出力されました。__ 確認すると、CloudFront ディストリビューションオリジンの Access-Control-Allow-Origin ヘッダーが www.example.com に設定されていました。エラーを解決するために何をすべきでしょうか?

__API Gateway API エンドポイントで CORS 設定を有効にする。__ Access-Control-Allow-Origin ヘッダーが www.example.com に設定されているすべての応答を返すように API エンドポイントが構成されていることを確認する。 

## **CORSで、できること**

* 別ドメインにあるAPIからデータを取得する
* 異なるドメインにある画像を埋め込む
* 異なるドメインにあるフレームを表示する

## **CORSの仕組み**

1. クライアント側が、異なるオリジンにあるリソースへリクエストを送信します。
2. サーバー側は、`Access-Control-Allow-Origin`ヘッダーを使用して、どのオリジンからのアクセスを許可するかを指定します。
3. ブラウザは、`Access-Control-Allow-Origin`ヘッダーの内容を確認し、リクエストが許可されているかどうかを判断します。
4. リクエストが許可されている場合、ブラウザはリソースをクライアント側に返します。
5. リクエストが許可されていない場合、ブラウザはエラーを発生させます。

**CORSを利用する際の注意事項**

* サーバー側で、適切なCORSヘッダーを設定する必要がある。
* クライアント側では、CORSエラーを適切に処理する必要がある。

**CORS ヘッダー**

* **Origin** クライアントのリクエストオリジンを示すヘッダー。
* **Access-Control-Request-Headers** クライアントが送信する追加の HTTP ヘッダーをリストするヘッダー。
* **Access-Control-Request-Method** クライアントが使用する HTTP メソッド (GET、POST、PUT など) を示すヘッダー。
* **Access-Control-Allow-Origin** どのオリジンからのリクエストが許可されるかを指定するヘッダー。
* **Access-Control-Allow-Methods** どの HTTP メソッドが許可されるかを指定するヘッダー。
* **Access-Control-Allow-Headers** どの追加の HTTP ヘッダーが許可されるかを指定するヘッダー。
* **Access-Control-Max-Age** プリフライトリクエストの結果をキャッシュする期間を秒単位で指定するヘッダー。

**プリフライトリクエスト**

一部のブラウザは、本番のリクエストを送信する前に、プリフライトリクエストと呼ばれるオプションのリクエストを送信します。プリフライトリクエストは、サーバーが CORS リクエストを許可するかどうかを確認するために使用されます。

## **CORS の実装**

JavaScript で CORS を実装するには、いくつかの方法があります。

**Fetch API** Fetch API は、非同期 HTTP リクエストを行うための標準的な JavaScript API です。Fetch API には、CORS リクエストを自動的に処理する機能が組み込まれています。
**XMLHttpRequest** XMLHttpRequest は、以前によく使用されていた非同期 HTTP リクエストを行うための JavaScript API です。XMLHttpRequest を使用するには、CORS ヘッダーを手動で設定する必要があります。
**ライブラリ** Axios や jQuery などのライブラリを使用して、CORS リクエストを簡単に実装できます。これらのライブラリは、Fetch API または XMLHttpRequest を使用して、CORS ヘッダーを自動的に処理します。

## Fetch APIとは

Fetch APIは、JavaScriptでHTTPリクエストを行うためのシンプルなインターフェースを提供。Promiseベースの設計により、非同期操作を簡単に扱うことができ、コードの可読性と保守性が向上。GETリクエストやPOSTリクエストなど、さまざまなHTTPメソッドをサポートしており、最新のブラウザで広くサポート。Fetch APIを使用することで、Webアプリケーションのネットワークリクエストを効率的に管理出来る。

### Fetch APIの利点

1. **Promiseベース**
Promiseベースであるため、モジュール化しやすい。Promiseチェーンやasync/awaitを使用して、コードが読みやすく、メンテナンスしやすい。Fetch APIはPromiseを使用して非同期操作を扱うため、コールバック地獄を避けることができる。

2. **シンプル**
Fetch APIは、シンプルで直感的なインターフェースを提供します。HTTPリクエストを行うためのコードが簡潔で理解しやすくなります。Promiseの`then`と`catch`を使うことで、エラー処理を簡単に記述できる。

3. **柔軟性**
Fetch APIは、GETリクエストだけでなく、POST、PUT、DELETEなどの他のHTTPメソッドもサポート。また、リクエストヘッダーやボディの設定も簡単に行える。

4. **標準化**
Fetch APIは、最新のブラウザで広くサポートされており、標準的な方法として推奨されている。

### Fetch API を使用して CORS リクエストを行う方法
`https://example.com/api/data` エンドポイントに GET リクエストを送信し、JSON レスポンスを解析しています。このエンドポイントは異なるオリジンにあるため、CORS ヘッダーが自動的に追加されます。CORS は、JavaScript で異なるオリジン間でリソースを共有するための強力なメカニズムです。Fetch API やライブラリを使用することで、CORS リクエストを簡単に実装できます。

```javascript
fetch('https://example.com/api/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error));
```

Fetch APIは、GETリクエストだけでなく、POST、PUT、DELETEなどのリクエストにも対応しています。また、オプションオブジェクトを使って、リクエストヘッダーやリクエストボディを設定することもできます。

### Fetch APIとXMLHttpRequestの違い

| 項目 | Fetch API | XMLHttpRequest |
|---|---|---|
| インターフェース | シンプルで使いやすい | 複雑で分かりにくい |
| 非同期処理 | Promiseベース | コールバックベース |
| エラー処理 | 容易 | 複雑 |
| モジュール化 | しやすい | 難しい |
| サポート状況 | 最新のブラウザで広くサポートされている | 古いブラウザではサポートされていない場合がある |


### Fetch APIの基本的な使い方

#### GETリクエスト

```javascript
fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json();
  })
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error('There was a problem with the fetch operation:', error);
  });
```

**fetch(url)** 指定したURLに対してHTTPリクエストを行います。デフォルトではGETリクエストが行われます。
**response.ok** レスポンスが正常であるかどうかを示します。ステータスコードが200-299の範囲内であればtrueを返します。
**response.json()** レスポンスボディをJSON形式でパースします。これはPromiseを返します。
**.then()** Promiseが解決されたときに実行されるコールバック関数を指定します。
**.catch()** Promiseが拒否されたときに実行されるコールバック関数を指定します。

### POSTリクエスト

```javascript
fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'John Doe',
    age: 30
  })
})
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json();
  })
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error('There was a problem with the fetch operation:', error);
  });
```

**method** HTTPメソッドを指定します（例: 'POST', 'PUT', 'DELETE'など）。
**headers** リクエストヘッダーを指定します。ここでは、Content-Typeを'application/json'に設定しています。
**body** リクエストボディを指定します。ここでは、JSON.stringify()を使用してJavaScriptオブジェクトをJSON文字列に変換しています。



## Fetch APIの制限

1. **CORS制限**
Fetch APIは、同一オリジンポリシーに従います。異なるオリジンへのリクエストを行う場合、サーバーがCORS（Cross-Origin Resource Sharing）を適切に設定している必要があります。

2. **エラーハンドリング**
Fetch APIは、ネットワークエラーやリクエストの失敗をPromiseの拒否として扱いますが、HTTPステータスコードがエラー（例: 404, 500）の場合でもPromiseは解決されます。そのため、response.okを使用してレスポンスのステータスを確認する必要があります。

#### ご参考になれば幸いです。