「OK, but no(OKですが、○○がありません)」
AJAX呼び出しを使って作業したことがある方ならば、おそらくブラウザコンソールに表示される以下のようなエラーをご存知でしょう。
https://example.com/を読み込めませんでした。要求されたリソースに「Access-Control-Allow-Origin」ヘッダーが存在しません。そのため、Origin「https://anfo.pl」のアクセスが許可されていません。不透明な応答が必要な場合は、リクエストのモードを「no-cors」に設定して、CORSを無効にしてリソースを取り出してください。
このメッセージが表示された場合、応答は失敗したものの、[ネットワーク]タブに移動すると返されたデータが表示されることを意味します。
クロスオリジンリソースシェアリング(CORS)
この振る舞いは、ブラウザCORSの実装の影響です。
CORSが標準化される前に、セキュリティ上の理由から、異なるドメインのAPIエンドポイントを呼び出す方法はありませんでした。これは、Same-Origin Policy(同一オリジンポリシー)によってブロックされていました(現在もある程度ブロックされています)。
CORSは、あなたが行ったリクエストを許可すると同時に、不正なJSによって行われたリクエストをブロックするためのメカニズムです。以下へのHTTPリクエストを行うたびにトリガーされます。
・別のドメイン(例:example.comのサイトがapi.comを呼び出すこと)
・別のサブドメイン(例:example.comのサイトがapi.example.comを呼び出すこと)
・別のポート(例:example.comのサイトがexample.com:3001を呼び出すこと)
・別のプロトコル(例:https://example.comのサイトがhttp://example.comを呼び出すこと)
このメカニズムは、様々なウェブサイト(例:Google Ads経由で表示される広告)にスクリプトを埋め込む攻撃者が、*あなたの*の資格情報を使ってトランザクションを作成してログインした場合に、www.yourbank.comへのAJAX呼び出しを行うのを防ぐことができます。
サーバーが「単純な」GETまたはPOSTリクエストに対して特定のヘッダーで応答しない場合、リクエストは送信され、データも受信されますが、ブラウザがJavaScriptの応答へのアクセスを許可しません。
あなたのブラウザが「単純ではない」リクエスト(例:クッキーを含むリクエスト、またはContent-typeがapplication/x-ww-form-urlencoded、multipart/form-dataまたはtext-plain以外のリクエスト)を出そうとしている場合、preflightと呼ばれるメカニズムが使用され、OPTIONSリクエストがサーバーに送信されます。
「単純ではない」リクエストの一般的な例は、クッキーまたはカスタムヘッダーを追加したものです。あなたのブラウザがそのようなリクエストを送信し、サーバーが正しく応答しない場合、preflight呼び出しのみが行われます(余分なヘッダーなし)が、ブラウザが意図した実際のHTTPリクエストは送信されません。
Access-Control-Allow-○○?
CORSはリクエストと応答の両方でいくつかのHTTPヘッダーを使用します。しかし、作業を続けるために理解しなければならないものは以下のとおりです。
Access-Control-Allow-Origin
このヘッダーはサーバーによって返されることを意図しており、リソースにアクセスできるクライアントドメインを示します。値は以下のいずれかです。
・* - 任意のドメインをすべて許可する
・完全修飾ドメイン名(例:https://example.com)
クライアントが認証ヘッダー(例:クッキー)を渡す必要がある場合、値を*にすることはできません。完全修飾ドメインにしなければなりません。
Access-Control-Allow-Credentials
このヘッダーは、サーバーがクッキーによる認証をサポートしている場合、応答にのみ存在する必要があります。この場合の唯一の有効な値はtrueです。
Access-Control-Allow-Headers
サーバーがサポートしようとしているリクエストヘッダー値のコンマ区切りのリストを提供します。カスタムヘッダーを使用する場合(例:x-authentication-token)、OPTIONS呼び出しに対するこのACAヘッダー応答でそれを返す必要があります。さもないと、リクエストはブロックされます。
Access-Control-Expose-Headers
同様に、この応答には、呼び出しに対する実際の応答に存在するヘッダーのリストが含まれている必要があり、クライアントが使用できるようにする必要があります。その他のヘッダーはすべて制限されます。
Access-Control-Allow-Methods
サーバーがサポートしようとしているHTTPリクエストタイプの動詞(例:GET、POST)のコンマ区切りのリストです。
オリジン
このヘッダーは、クライアントが出しているリクエストの一部であり、アプリケーションが開始されたドメインが含まれます。セキュリティ上の理由から、ブラウザはこの値を上書きすることはできません。
CORS「エラー」の修正方法
CORSの振る舞いはエラーではないことを理解しておく必要があります。これは、あなたのユーザー、あなた自身、またはあなたが呼び出しているサイトを保護するために、期待どおりに機能するメカニズムです。
適切なヘッダーが欠如しているのは、間違ったクライアント側の実装(例:APIキーなどの認証データの欠落)のせいである場合があります。
あなたが直面しているシナリオに応じて、「エラーを修正する」方法がいくつかあります。
A - フロントエンドを開発しており、バックエンドを開発している人が部下または知り合いの場合
これが最良のシナリオです。あなたが呼び出しているサーバーで適切なCORSの応答を実装できるはずです。 APIがNode向けのexpressを使用している場合は、単純なcorsパッケージを使用できます。サイトを適切に保護したい場合は、Access-Control-Allow-Originヘッダーにホワイトリストを使用することを検討してください。
B - フロントエンドを開発しているが、現在バックエンドを制御する権限を持っておらず、一時的な解決策が必要な場合
これは2番目に良いシナリオです。なぜなら、ほとんどAパターンと同じで、少しの時間的な制約があるだけだからです。この問題を一時的に解決するには、ブラウザにCORSのメカニズムを無視させることができます。例えば、ACAO Chrome拡張機能を使用するか、Chromeを以下のフラグで実行してCORSのメカニズムを完全に無効にすることができます。
chrome --disable-web-security --user-data-dir
重要:これにより、ブラウザセッション全体の間、すべてのウェブサイトのメカニズムが無効になることを忘れないでください。慎重に使用してください。
もう1つの選択肢は、devServer.proxyを使用するか(webpackを使用してアプリケーションを提供していることを前提としています)、https://cors-anywhere.herokuapp.com/のようなCORS-as-a-service(CORSに基づいたサービス)ソリューションを使用することです。
C - フロントエンドを開発しており、バックエンドを制御する権限を持っておらず、これからも制御することはない場合
さて、問題が複雑になってきました。まず、サーバーが適切なヘッダーを送信していない理由を確認する必要があります。
もしかすると、サードパーティのアプリがそのAPIにアクセスすることを許可していないのかもしれません。もしかすると、そのAPIはブラウザ側ではなく、サーバー側のアプリケーションでのみ使用できるように意図されているのかもしれません。もしかすると、URLにソートの許可トークンを送信する必要があるのかもしれません。
あなたがまだブラウザ経由でデータにアクセスできるはずだと思う場合は、解決策Bパターンで行ったのと同様に、ブラウザアプリケーションとAPIの間にある独自のプロキシを作成する必要があります。
間にプロキシを追加する
プロキシは、プロキシ自体がクライアントとの通信時にCORSを適切にサポートしている限り、アプリケーションと同じドメインで実行する必要はありません。プロキシとAPIとの間の通信が、CORSをサポートしている必要性は全くありません。
独自のプラットフォームを作成してもいいですし、https://www.npmjs.com/package/cors-anywhereのような既成のソリューションを使用しても構いません。
このようなアプローチは、資格情報をサポートしたい場合に、セキュリティ上のリスクを引き起こす可能性があることに注意してください。
CORSのさらなる詳細
CORSについてさらに知りたい場合は、詳細なMDNの記事をご覧ください。
CREDIT:原著者の許諾のもと翻訳・掲載しています。
[原文]Understanding CORS (Posted Jan 28, 2018) by Bartosz Szczeciński