クロスオリジンリソースシェアリングとプリフライトリクエストが必要な理由
1. 序章
最近、私たちがアクセスするWebページは、私たちが目にするデータを提供するために、さまざまなサーバーに頻繁にリクエストを送信します。 これはクロスオリジンリソースシェアリング(CORS)と呼ばれ、このチュートリアルでは、それが何であるか、CORSポリシーがブラウザーにどのように実装されているか、およびプリフライトリクエストがある理由について説明します。
2. 同一生成元ポリシーとは何ですか?
CORSについて説明する前に、一歩下がって同一生成元ポリシー(SOP)を紹介しましょう。 あるオリジンのスクリプトが別のオリジン(クロスオリジン)のリソースと対話する方法を制限する、ブラウザーのデフォルトのセキュリティ対策。
SOPコンテキストでは、2つのURLが同じスキーム、ドメイン、およびポート番号を持っている場合(URLにポートが含まれている場合)、同じオリジンであると見なされます。たとえば、 https://www.baeldung.com と https://www.baeldung.com/about 同じ起源を持っていますが、 https://www.baeldung.comと http://www.baeldung.com スキームの違いのためにしないでください。
同一生成元ポリシーを実装するブラウザーは、XMLHttpRequestやFetchAPI などのメソッドを使用して、あるオリジンで提供されるWebサイトスクリプトが別のオリジンにリクエストを送信することを制限します。
クロスサイトリクエストフォージェリ攻撃から保護するための最初の防衛線として、同一生成元ポリシーが導入されました。 ただし、最終的に、開発者は、異なるドメインのAPIからリソースを要求し、別のパーティが提供する機能を利用できると便利であることに気付きました。 このため、CORSポリシーは提案されました その後、特定の条件下でオリジン間リソース共有を可能にするために導入されました。 しましょう これがどのように発生するかについては、次のセクションで詳しく説明します。
3. CORSとは何ですか?
サードパーティのAPIにアクセスする必要性に対処するために、 CORSポリシーは、あるオリジンが提供するスクリプトが別のオリジンのリソースをリクエストする方法を決定します。 CORSポリシーは、リクエスト/レスポンスに含める必要のある特定のHTTPヘッダーを定義します交流; サーバーがどのオリジンからのリクエストを許可するかをサーバーが通信できるようにします。 次に、ブラウザーは、スクリプトが応答にアクセスすることを許可または禁止することにより、これを強制します。
クロスオリジンリクエストに関しては、ブラウザが処理する3つのタイプがあります。
3.1. 簡単なリクエスト
単純なリクエストは、次の条件を満たすリクエストです。
- GET、HEAD、またはPOSTリクエストです
- 自動User-AgentヘッダーまたはAccept、 Accept-Language 、
Content-Language[などのCORSセーフリストヘッダーのみを送信しますX156X]、コンテンツタイプ - Content-Type ヘッダーには、 application / x-www-form-urlencoded 、 multipart / form-data 、および text/plainの値のみを含めることができます
- ReadableStreamオブジェクトを使用しません
- XMLHttpRequest.upload イベントリスナーが接続されていません
このJavascriptのスニペットによって開始された次の単純なCORSリクエストを見てみましょう。 https://www.site.com:
const xhr = new XMLHttpRequest();
const url = 'https://www.api.com?q=test';
xhr.open('GET', url);
xhr.onreadystatechange = requestHandler;
xhr.send();
これにより、ブラウザとhttps://www.api.com:の間で次の交換が生成されます。
- まず、ブラウザは origin ヘッダーを使用してリクエストを送信し、発信元を識別して https://に送信します。www.api.com サーバ
- サーバーは要求されたデータで応答し、 access-control-allow-originヘッダーがhttps://www.site.comに設定されています。 ]これは、サーバーがこのオリジンからのリクエストを許可することをブラウザーに示します
- ブラウザは、 requestHandler メソッドが応答データにアクセスできるようにすることで、これを強制します
この交換を下の画像に要約できます。
access-control-allow-origin ヘッダーは、サーバーが許可するリクエストを表示するためにサーバーが使用できる主要なCORSヘッダーの1つです。 このヘッダーの値は、特定のオリジンへのアクセスを許可するようにブラウザーに指示する単一のオリジンにすることも、ブラウザーに任意のオリジンを許可するように指示する*にすることもできます。
サーバーがこのヘッダーで応答しない場合、またはヘッダー値がリクエストの発信元と一致しないドメインである場合。 その後、ブラウザは応答がスクリプトに戻されないようにします。 これにより、コンソールエラーが発生する可能性があります。たとえば、次の画像は、GETリクエストを送信しようとした場合にChromeブラウザが生成するエラーの種類を示しています。 http://www.google.com 私たちのウェブサイトから:
3.2. 単純でないリクエスト
単純な要求ではない要求は、単純でない要求またはプリフライトされた要求と見なされます。ブラウザーは、これらの種類の要求を少し異なる方法で処理します。 実際のリクエストを送信する前に、ブラウザはプリフライトリクエストと呼ばれるものを送信し、このタイプのリクエストが許可されているかどうかをサーバーに確認します。 プリフライトリクエストは、次のヘッダーを含むOPTIONSリクエストです。
- origin –リクエストの送信元をサーバーに通知します
- access-control-request-method –リクエストが実装するHTTPメソッドをサーバーに通知します
- access-control-request-headers –リクエストに含まれるヘッダーをサーバーに通知します
これに応答して、サーバーは、次のヘッダーで応答することにより、このオリジンからのこの種の要求を受け入れるかどうかを決定できます。
- access-control-allow-origin –サーバーが許可するオリジン
- access-control-allow-methods –サーバーが許可するメソッドのコンマ区切りリスト
- access-control-allow-headers –サーバーが許可するヘッダーのコンマ区切りリスト
- access-control-max-age –プリフライトリクエストへの応答をキャッシュする時間(秒単位)をブラウザに指示します
- 可能なCORS応答ヘッダーの完全なリストは、 MDN WebDocsにリストされています。
単純なリクエストと同様に、サーバーにCORSヘッダーが含まれていない場合、ブラウザーはこのサーバーがこのリクエストを許可していないと見なし、実際のリクエストを続行しません。
以前のリクエストを少し変更して、これが実際に動作していることを見てみましょう。カスタムヘッダーを追加すると、シンプルではなくなります。
const xhr = new XMLHttpRequest();
const url = 'https://www.api.com?q=test';
xhr.open(‘GET', url);
xhr.setRequestHeader(‘custom-header', ’test')
xhr.onreadystatechange = requestHandler;
xhr.send();
ブラウザはこのリクエストを単純ではないと識別し、サーバーへのプリフライトリクエストを開始して、それが許可されているかどうかを確認します。 https://www.api.com サーバーがこの種のリクエストを許可した場合、このインタラクションがどのように見えるかを見てみましょう。
ご覧のとおり、サーバーは正しいヘッダーで応答し、ブラウザーは引き続き実際の要求を行いました。 サーバーが必要なヘッダーなしで応答した場合、ブラウザーは要求が送信されないようにします。
ここに、カスタムヘッダーを含む非単純なリクエストを使用してGoogleBookAPIにアクセスしようとしているブラウザの例があります。 APIが必要なヘッダーを使用してプリフライトリクエストに応答しなかったため、ブラウザコンソールにわずかに異なるエラーが表示されます。
3.3. 資格のあるリクエスト
資格情報には、Cookie、承認ヘッダー、またはTLSクライアント証明書を使用できます。 デフォルトでは、CORSポリシーでは、リクエストにクレデンシャルを含めるフラグが含まれていて、サーバーが access-control-allow-credentials をtrueに設定して応答しない限り、クロスオリジンリクエストにクレデンシャルを含めることはできません。 。
リクエストにクレデンシャルを含めるには、 withCredentials プロパティをtrueに設定して、XMLHttpRequestを更新しましょう。
const xhr = new XMLHttpRequest();
const url = 'https://www.api.com?q=test';
xhr.open('GET', url);
xhr.withCredentials = true;
xhr.send();
サーバーの応答に、trueに設定された access-control-allow-credentials と、リクエストの送信元と同じオリジンに一致するaccess-control-allow-originヘッダーが含まれていない場合; ブラウザはリクエストをブロックします。 この例を以下に示します。ここでは、資格情報を許可しないGoogleBookAPIに同じリクエストを送信しようとしています。
4. プリフライトリクエストの背後にある理由は何ですか?
SOPがしばらく実施された後にCORSが提案されたため、プリフライトリクエストを使用するというアイデアにはいくつかの利点がありました。 プリフライトリクエストを使用すると、サーバーはリクエストが実行される前にリクエストを調べて、リクエストが許可されているかどうかを示す機会を得ることができます。
サーバーが別のオリジンから許可しない特定のリクエストに副作用がある場合。 プリフライトリクエストは、サーバーが拒否を示すヘッダーで応答した場合に最初にチェックしてリクエストをブロックすることにより、同意のないサーバーを保護するのに役立ちます。 これに加えて、serversは、開発時に許可するリクエストとヘッダーの種類を変更する場合があります。 プリフライトリクエストが設定されていると、ブラウザはこれを確認し、それに応じて調整できます。
最後に、CORSには下位互換性があります。 SOPに依存していて、CORSを処理しない一部の古いサーバーは、この方法を使用して保護されます。これは、ブラウザーがCORSヘッダーを送信しないサーバーを、同じオリジンからのリクエストのみを許可するサーバーと同じように扱うためです。
5. 結論
このチュートリアルでは、クロスオリジンリソースポリシーとブラウザがそれを実装する方法について詳しく見てきました。 CORSを実装するブラウザは、リクエストを3つの主要なタイプとして扱います。 単純な要求、プリフライトされた要求、および単純またはプリフライトされた資格のある要求。