diff --git a/files/ja/web/http/cors/cors_principle.png b/files/ja/web/http/cors/cors_principle.png deleted file mode 100644 index 31e6382e116e7d..00000000000000 Binary files a/files/ja/web/http/cors/cors_principle.png and /dev/null differ diff --git a/files/ja/web/http/cors/cred-req-updated.png b/files/ja/web/http/cors/cred-req-updated.png deleted file mode 100644 index 87834b5356be80..00000000000000 Binary files a/files/ja/web/http/cors/cred-req-updated.png and /dev/null differ diff --git a/files/ja/web/http/cors/fetching-page-cors.svg b/files/ja/web/http/cors/fetching-page-cors.svg new file mode 100644 index 00000000000000..09b90885868409 --- /dev/null +++ b/files/ja/web/http/cors/fetching-page-cors.svg @@ -0,0 +1,308 @@ + + + + + + + + + + + + + + 画像 + + + + + 画像 + + + + + + + + + + + フォントと domain-b.com からの + + + 画像のあるキャンバス + + + + + + フォントと domain-b.com からの画像のあるキャンバス + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ウェブ文書 + + + + + ウェブ文書 + + + + + + + + + + + + + + + + + + + + + + + + + + + GET /index.html + + + + + + GET /index.html + + + + + + + + + + + + GET styles.css + + + + + + + GET styles.css + + + + + + + + + + + GET header.png + + + + + + GET header.png + + + + + + + + + + + GET image.png + + + + + + GET image.png + + + + + + + + + + + GET font.woff + + + + + + GET font.woff + + + + + + + + + + ウェブサーバー + + + domain-a.com + + + + + + ウェブサーバー + + + + + + + + + + + + + + ウェブサーバー + + + domain-b.com + + + + + + ウェブサーバー + + + + + + + + + + + + メインリクエスト: オリジンを定義 + + + + + + メインリクエスト: オリジンを定義 + + + + + + + + + + + domain-a.com + + + + + + domain-a.com + + + + + + + + + + + + + 同一オリジンリクエストは常に許可される + + + + + + + 同一オリジンリクエストは常に許可される + + + + + + + + + + + CORS によって制御されたオリジン間リクエスト + + + + + + + CORS によって制御されたオリジン間リクエスト + + + + + + + + diff --git a/files/ja/web/http/cors/include-credentials.svg b/files/ja/web/http/cors/include-credentials.svg new file mode 100644 index 00000000000000..6ea49a16226e53 --- /dev/null +++ b/files/ja/web/http/cors/include-credentials.svg @@ -0,0 +1,65 @@ + + + + サーバー + + + + クライアント + + + + + + サーバー + + + + + + + クライアント + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GET /resources/credentialed-content/ HTTP/1.1 + Origin: https://foo.example + Cookie: pageAccess=2 + + HTTP/1.1 200 OK + Access-Control-Allow-Origin: https://foo.example + Access-Control-Allow-Credentials: true + + diff --git a/files/ja/web/http/cors/index.md b/files/ja/web/http/cors/index.md index be2aaeca014acc..a3f85a2856f0f7 100644 --- a/files/ja/web/http/cors/index.md +++ b/files/ja/web/http/cors/index.md @@ -2,36 +2,36 @@ title: オリジン間リソース共有 (CORS) slug: Web/HTTP/CORS l10n: - sourceCommit: 185d871fd1c78b6c762d4a703b269dafb3167bbb + sourceCommit: f75b2c86ae4168e59416aed4c7121f222afc201d --- {{HTTPSidebar}} オリジン間リソース共有 (Cross-Origin Resource Sharing, {{Glossary("CORS")}}) は、追加の {{Glossary("HTTP")}} ヘッダーを使用して、ある{{glossary("origin", "オリジン")}}で動作しているウェブアプリケーションに、異なるオリジンにある選択されたリソースへのアクセス権を与えるようブラウザーに指示するための仕組みです。ウェブアプリケーションは、自分とは異なるオリジン (ドメイン、プロトコル、ポート番号) にあるリソースをリクエストするとき、オリジン間 HTTP リクエストを実行します。 -オリジン間リクエストとは、例えば `https://domain-a.com` で提供されているウェブアプリケーションのフロントエンド JavaScript コードが {{domxref("XMLHttpRequest")}} を使用して `https://domain-b.com/data.json` へリクエストを行うような場合です。 +オリジン間リクエストとは、例えば `https://domain-a.com` で提供されているウェブアプリケーションのフロントエンド JavaScript コードが {{domxref("Window/fetch", "fetch()")}} を使用して `https://domain-b.com/data.json` へリクエストを行うような場合です。 -セキュリティ上の理由から、ブラウザーは、スクリプトによって開始されるオリジン間 HTTP リクエストを制限しています。例えば、 `XMLHttpRequest`や [Fetch API](/ja/docs/Web/API/Fetch_API) は[同一オリジンポリシー](/ja/docs/Web/Security/Same-origin_policy) (same-origin policy) に従います。つまり、これらの API を使用するウェブアプリケーションは、そのアプリケーションが読み込まれたのと同じオリジンに対してのみリソースのリクエストを行うことができ、それ以外のオリジンからの場合は正しい CORS ヘッダーを含んでいることが必要です。 +セキュリティ上の理由から、ブラウザーは、スクリプトによって開始されるオリジン間 HTTP リクエストを制限しています。例えば、`fetch()` や {{domxref("XMLHttpRequest")}} は[同一オリジンポリシー](/ja/docs/Web/Security/Same-origin_policy) (same-origin policy) に従います。つまり、これらの API を使用するウェブアプリケーションは、そのアプリケーションが読み込まれたのと同じオリジンに対してのみリソースのリクエストを行うことができ、それ以外のオリジンからの場合は正しい CORS ヘッダーを含んでいることが必要です。 -![CORS の仕組みの図式化](cors_principle.png) +![CORS の仕組みの図式化](fetching-page-cors.svg) -CORS の仕組みは、安全なオリジン間のリクエストとブラウザー・サーバー間のデータ転送を支援します。最近のブラウザーは CORS を `XMLHttpRequest` や [Fetch](/ja/docs/Web/API/Fetch_API) などの API で使用して、オリジン間 HTTP リクエストのリスクの緩和に役立てています。 +CORS の仕組みは、安全なオリジン間のリクエストとブラウザー・サーバー間のデータ転送を支援します。最近のブラウザーは CORS を `fetch()` や `XMLHttpRequest` などの API で使用して、オリジン間 HTTP リクエストのリスクの緩和に役立てています。 ## CORS を使用したリクエストとは この [cross-origin sharing standard](https://fetch.spec.whatwg.org/#http-cors-protocol) では、以下についてオリジン間の HTTP リクエストができるようにしています。 -- 前述のような {{domxref("XMLHttpRequest")}} または [Fetch API](/ja/docs/Web/API/Fetch_API) の呼び出し。 -- ウェブフォント (CSS の `@font-face` で別ドメインのフォントを利用するため)。[これによりサーバーは、許可したウェブサイトのみからオリジンをまたがって読み込んで利用できる TrueType フォントを提供することができます。](https://www.w3.org/TR/css-fonts-3/#font-fetching-requirements) +- 前述のような `fetch()` や `XMLHttpRequest` の呼び出し。 +- ウェブフォント(CSS の `@font-face` で別ドメインのフォントを利用するため)。[これによりサーバーは、許可したウェブサイトのみからオリジンをまたがって読み込んで利用できる TrueType フォントを提供することができます。](https://www.w3.org/TR/css-fonts-3/#font-fetching-requirements) - [WebGL テクスチャ](/ja/docs/Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL)。 - {{domxref("CanvasRenderingContext2D.drawImage()", "drawImage()")}} を使用してキャンバスへ描かれた画像や映像のフレーム -- [画像から生成した CSS シェイプ](/ja/docs/Web/CSS/CSS_Shapes/Shapes_From_Images)。 +- [画像から生成した CSS シェイプ](/ja/docs/Web/CSS/CSS_shapes/Shapes_from_images)。 この記事では、 HTTP ヘッダーの要件を含むオリジン間リソース共有の全般的な説明を行います。 ## 機能概要 -オリジン間リソース共有の仕様は、ウェブブラウザーから情報を読み取ることを許可されているオリジンをサーバーが記述することができる、新たな [HTTP ヘッダー](/ja/docs/Web/HTTP/Headers)を追加することで作用します。加えて仕様書では、サーバーの情報に副作用を引き起こすことがある HTTP のリクエストメソッド (特に {{HTTPMethod("GET")}} 以外の HTTP メソッドや、特定の [MIME タイプ](/ja/docs/Web/HTTP/Basics_of_HTTP/MIME_types)を伴う {{HTTPMethod("POST")}}) のために、ブラウザーが HTTP の {{HTTPMethod("OPTIONS")}} リクエストメソッドを用いて、あらかじめリクエストの「プリフライト」 (サーバーから対応するメソッドの一覧を収集すること) を行い、サーバーの「認可」のもとに実際のリクエストを送信することを指示しています。サーバーはリクエスト時に「資格情報」 ([Cookie](/ja/docs/Web/HTTP/Cookies) や [HTTP 認証](/ja/docs/Web/HTTP/Authentication)など) を送信するべきかをクライアントに伝えることもできます。 +オリジン間リソース共有の仕様は、ウェブブラウザーから情報を読み取ることを許可されているオリジンをサーバーが記述することができる、新たな [HTTP ヘッダー](/ja/docs/Web/HTTP/Headers)を追加することで作用します。加えて仕様書では、サーバーの情報に副作用を引き起こすことがある HTTP のリクエストメソッド (特に {{HTTPMethod("GET")}} 以外の HTTP メソッドや、特定の [MIME タイプ](/ja/docs/Web/HTTP/MIME_types)を伴う {{HTTPMethod("POST")}}) のために、ブラウザーが HTTP の {{HTTPMethod("OPTIONS")}} リクエストメソッドを用いて、あらかじめリクエストの「プリフライト」 (サーバーから対応するメソッドの一覧を収集すること) を行い、サーバーの「認可」のもとに実際のリクエストを送信することを指示しています。サーバーはリクエスト時に「資格情報」 ([Cookie](/ja/docs/Web/HTTP/Cookies) や [HTTP 認証](/ja/docs/Web/HTTP/Authentication)など) を送信するべきかをクライアントに伝えることもできます。 CORS は様々なエラーで失敗することがありますが、セキュリティ上の理由から、エラーについて *JavaScript から知ることができない*よう定められています。コードからはエラーが発生したということしか分かりません。何が悪かったのかを具体的に知ることができる唯一の方法は、ブラウザーのコンソールで詳細を見ることです。 @@ -39,13 +39,13 @@ CORS は様々なエラーで失敗することがありますが、セキュリ ## アクセス制御シナリオの例 -オリジン間リソース共有の動作の仕組みを説明する 3 つのシナリオを示します。これらの例はすべて {{domxref("XMLHttpRequest")}} を用いており、対応しているブラウザーにおいて、オリジンをまたいでアクセスすることができます。 +オリジン間リソース共有の動作の仕組みを説明する 3 つのシナリオを示します。これらの例はすべて {{domxref("Window/fetch", "fetch()")}} を用いており、対応しているブラウザーにおいて、オリジンをまたいでアクセスすることができます。 ### 単純リクエスト リクエストによっては {{Glossary("Preflight_request","CORS プリフライト")}}を発生させません。これをこの記事では古い [CORS 仕様書](https://www.w3.org/TR/2014/REC-cors-20140116/#terminology)に倣って*単純リクエスト*と呼んでいますが、 (現在 CORS を定義している) [Fetch 仕様書](https://fetch.spec.whatwg.org/) ではこの用語を使用していません。 -その動機は、HTML 4.0 からの {{HTMLElement("form")}} 要素(クロスサイト {{domxref("XMLHttpRequest")}} と {{domxref("fetch")}} に先行する)は、どのオリジンにでも単純なリクエストを送信できるので、サーバーを書く人はすでに {{Glossary("CSRF", "cross-site request forgery")}} (CSRF) から保護していなければならないからです。この仮定の下では、 CSRF の脅威はフォーム送信の脅威よりも悪くないので、サーバーはフォーム送信のように見えるすべてのリクエストを受け取ることを(プリフライトリクエストに応答することによって)オプトインする必要はないのです。しかし、サーバーは {{HTTPHeader("Access-Control-Allow-Origin")}} を使用して、レスポンスをスクリプトと共有することを選択する必要があります。 +その動機は、HTML 4.0 からの {{HTMLElement("form")}} 要素(サイト間 {{domxref("Window/fetch", "fetch()")}} と {{domxref("XMLHttpRequest")}} に先行する)は、どのオリジンにでも単純なリクエストを送信できるので、サーバーを書く人はすでに {{Glossary("CSRF", "cross-site request forgery")}} (CSRF) から保護していなければならないからです。この仮定の下では、 CSRF の脅威はフォーム送信の脅威よりも悪くないので、サーバーはフォーム送信のように見えるすべてのリクエストを受け取ることを(プリフライトリクエストに応答することによって)オプトインする必要はないのです。しかし、サーバーは {{HTTPHeader("Access-Control-Allow-Origin")}} を使用して、レスポンスをスクリプトと共有することを選択する必要があります。 *単純リクエスト*は、**以下のすべての条件を満たす**ものです。 @@ -63,9 +63,6 @@ CORS は様々なエラーで失敗することがありますが、セキュリ - {{HTTPHeader("Content-Type")}} (但し、下記の追加の要件に注意してください) - {{HTTPHeader("Range")}} ([単純範囲ヘッダー値](https://fetch.spec.whatwg.org/#simple-range-header-value)、例えば `bytes=256-` や `bytes=127-255` の場合) -> [!NOTE] -> Firefox は、まだ `Range` をセーフリストリクエストヘッダーとして実装していません。[バグ 1733981](https://bugzilla.mozilla.org/show_bug.cgi?id=1733981)を参照してください。 - - {{HTTPHeader("Content-Type")}} ヘッダーで指定できる{{Glossary("MIME type", "メディア種別")}}に許されるタイプ/サブタイプの組み合わせは、以下のもののみです。 - `application/x-www-form-urlencoded` @@ -78,26 +75,27 @@ CORS は様々なエラーで失敗することがありますが、セキュリ > [!NOTE] > WebKit Nightly および Safari Technology Preview は、 {{HTTPHeader("Accept")}}、{{HTTPHeader("Accept-Language")}}、{{HTTPHeader("Content-Language")}} ヘッダーの値に追加の制限を掛けています。これらのヘッダーが「標準外」の値の場合、 WebKit/Safari はそのリクエストが「単純リクエスト」の条件に合うとは判断しません。 WebKit/Safari がこれらのヘッダーのどの値を「標準外」と判断するかについては、以下の WebKit のバグを除いて文書化されていません。 > -> - [Require preflight for non-standard CORS-safelisted request headers Accept, Accept-Language, and Content-Language](https://bugs.webkit.org/show_bug.cgi?id=165178) -> - [Allow commas in Accept, Accept-Language, and Content-Language request headers for simple CORS](https://bugs.webkit.org/show_bug.cgi?id=165566) -> - [Switch to a blacklist model for restricted Accept headers in simple CORS requests](https://bugs.webkit.org/show_bug.cgi?id=166363) +> - [Require preflight for non-standard CORS-safelisted request headers Accept, Accept-Language, and Content-Language](https://webkit.org/b/165178) +> - [Allow commas in Accept, Accept-Language, and Content-Language request headers for simple CORS](https://webkit.org/b/165566) +> - [Switch to a blacklist model for restricted Accept headers in simple CORS requests](https://webkit.org/b/166363) > > これは仕様の一部ではないので、他のブラウザーはこの追加の制限を実装していません。 -例えば、ドメイン `https://foo.example` にあるウェブコンテンツがドメイン `https://bar.other` にあるコンテンツを呼び出したいとします。以下のようなコードが `foo.example` 内の JavaScript で使用されるかもしれません。 +例えば、`https://foo.example` のウェブコンテンツが JSON コンテンツをドメイン `https://bar.other` から取得したいとします。 この種のコードは、`foo.example` で展開された JavaScript で使用される可能性があります。 ```js -const xhr = new XMLHttpRequest(); -const url = "https://bar.other/resources/public-data/"; +const fetchPromise = fetch("https://bar.other"); -xhr.open("GET", url); -xhr.onreadystatechange = someHandler; -xhr.send(); +fetchPromise + .then((response) => response.json()) + .then((data) => { + console.log(data); + }); ``` この処理は、特権を扱うために CORS ヘッダーを使用して、クライアントとサーバーの間で簡単なデータ交換を行います。 -![Diagram of simple CORS GET request](simple-req.png) +![単純な CORS GET リクエストの図](simple-request.svg) この場合、ブラウザーがサーバーに何を送信し、サーバーが何を返すかを見てみましょう。 @@ -141,7 +139,7 @@ Access-Control-Allow-Origin: * Access-Control-Allow-Origin: https://foo.example ``` -> **メモ:** [資格情報を含むリクエスト](#資格情報を含むリクエスト)に応答する場合、サーバーは `Access-Control-Allow-Origin` ヘッダーにオリジンを値として指定する必要があり、"`*`" ワイルドカードを指定することはできません。 +> **メモ:** [資格情報を含むリクエスト](#資格情報を含むリクエスト)に応答する場合、サーバーは `Access-Control-Allow-Origin` ヘッダーにオリジンを値として指定する必要があり、`*` ワイルドカードを指定することはできません。 ### プリフライトリクエスト @@ -150,17 +148,24 @@ Access-Control-Allow-Origin: https://foo.example プリフライトが行われるリクエストの例です。 ```js -const xhr = new XMLHttpRequest(); -xhr.open("POST", "https://bar.other/doc"); -xhr.setRequestHeader("X-PINGOTHER", "pingpong"); -xhr.setRequestHeader("Content-Type", "text/xml"); -xhr.onreadystatechange = handler; -xhr.send("Arun"); +const fetchPromise = fetch("https://bar.other/doc", { + method: "POST", + mode: "cors", + headers: { + "Content-Type": "text/xml", + "X-PINGOTHER": "pingpong", + }, + body: "Arun", +}); + +fetchPromise.then((response) => { + console.log(response.status); +}); ``` 上記の例では、 `POST` で送信する XML の本体を作成しています。また、標準外の `X-PINGOTHER` HTTP リクエストヘッダーを設定しています。このようなヘッダーは HTTP/1.1 プロトコルに含まれていませんが、ウェブアプリケーションでは一般的に便利なものです。リクエストで `Content-Type` に `text/xml` を使用しており、かつ独自のヘッダーを設定しているため、このリクエストではプリフライトを行います。 -![プリフライトされたリクエストのイメージ図](preflight_correct.png) +![プリフライトされたリクエストのイメージ図](preflight-correct.svg) > [!NOTE] > 後述するように、実際の `POST` リクエストでは `Access-Control-Request-*` ヘッダーを含みません。`OPTIONS` リクエストのみで必要になります。 @@ -177,7 +182,7 @@ Accept-Encoding: gzip,deflate Connection: keep-alive Origin: https://foo.example Access-Control-Request-Method: POST -Access-Control-Request-Headers: X-PINGOTHER, Content-Type +Access-Control-Request-Headers: content-type,x-pingother HTTP/1.1 204 No Content Date: Mon, 01 Dec 2008 01:15:39 GMT @@ -191,16 +196,16 @@ Keep-Alive: timeout=2, max=100 Connection: Keep-Alive ``` -上記の 1 - 10 行目は {{HTTPMethod("OPTIONS")}} メソッドによるプリフライトリクエストを表します。ブラウザーは上記の JavaScript コードで使用していていたリクエストの引数に基づいて、プリフライトの送信が必要であることを判断します。これによりサーバーは実際のリクエストの引数によって送られるリクエストを受け入れ可能かを応答することができます。 OPTIONS は HTTP/1.1 のメソッドであり、サーバーから付加的な情報を得るために用いるもので、また{{Glossary("Safe/HTTP","安全")}}なメソッド、つまりリソースを変更するためには使用できないメソッドです。 OPTIONS リクエストと合わせて、他にリクエストヘッダーを 2 つ送信していることに注意してください (それぞれ 9 行目と 10 行目です)。 +最初のブロックは、{{HTTPMethod("OPTIONS")}} メソッドによるプリフライトリクエストを表します。ブラウザーは上記の JavaScript コードで使用していていたリクエストの引数に基づいて、プリフライトの送信が必要であることを判断します。これによりサーバーは実際のリクエストの引数によって送られるリクエストを受け入れ可能かを応答することができます。 OPTIONS は HTTP/1.1 のメソッドであり、サーバーから付加的な情報を得るために用いるもので、また{{Glossary("Safe/HTTP","安全")}}なメソッド、つまりリソースを変更するためには使用できないメソッドです。 OPTIONS リクエストと合わせて、他にリクエストヘッダーを 2 つ送信していることに注意してください。 ```http Access-Control-Request-Method: POST -Access-Control-Request-Headers: X-PINGOTHER, Content-Type +Access-Control-Request-Headers: content-type,x-pingother ``` {{HTTPHeader("Access-Control-Request-Method")}} ヘッダーは、プリフライトリクエストの一部として、実際のリクエストが `POST` リクエストメソッドで行われることをサーバーに通知します。 {{HTTPHeader("Access-Control-Request-Headers")}} ヘッダーは、実際のリクエストにカスタムヘッダーである `X-PINGOTHER` および Content-Type が含まれることをサーバーに通知します。ここでサーバーは、この状況下でリクエストの受け入れを望むかを判断する機会が与えられます。 -上記の 12 - 21 行目はサーバーが返すレスポンスであり、リクエストメソッド (`POST`) とリクエストヘッダー (`X-PINGOTHER`) が受け入れられることを示しています。特に、 15 - 18 行目を見てみましょう。 +上記の 2 番目のブロックはサーバーが返すレスポンスであり、リクエストメソッド (`POST`) とリクエストヘッダー (`X-PINGOTHER`) が受け入れられることを示しています。特に、次の行を見てみましょう。 ```http Access-Control-Allow-Origin: https://foo.example @@ -211,7 +216,7 @@ Access-Control-Max-Age: 86400 サーバーは `Access-Control-Allow-Origin: https://foo.example` により、アクセスをリクエストしているオリジンのドメインだけに限定することを返答しています。また、 `Access-Control-Allow-Methods` を返しており、これは当該リソースへの問い合わせで `POST` および `GET` が有効なメソッドであることを伝えます(なお、このヘッダーはレスポンスヘッダーの {{HTTPHeader("Allow")}} と似ていますが、アクセス制御でのみ使用されることに注意してください)。 -またサーバーは、 `Access-Control-Allow-Headers` を "`X-PINGOTHER, Content-Type`" の値で送信し、実際のリクエストで使用されるヘッダーを承認しています。 `Access-Control-Allow-Methods` と同様に、 `Access-Control-Allow-Headers` は受け入れ可能なヘッダーをカンマ区切りのリストで表します。 +またサーバーは、 `Access-Control-Allow-Headers` を `X-PINGOTHER, Content-Type` の値で送信し、実際のリクエストで使用されるヘッダーを承認しています。 `Access-Control-Allow-Methods` と同様に、 `Access-Control-Allow-Headers` は受け入れ可能なヘッダーをカンマ区切りのリストで表します。 最後に {{HTTPHeader("Access-Control-Max-Age")}} は、プリフライトリクエストを再び送らなくてもいいように、プリフライトのレスポンスをキャッシュしてよい時間を秒数で与えます。既定値は 5 秒です。この例では 86400 秒、つまり 24 時間です。なお、ブラウザーはそれぞれ[内部的な上限値](/ja/docs/Web/HTTP/Headers/Access-Control-Max-Age)を持っており、 `Access-Control-Max-Age` を超えた場合に制限を行います。 @@ -246,14 +251,14 @@ Keep-Alive: timeout=2, max=99 Connection: Keep-Alive Content-Type: text/plain -[Some XML payload] +[いくらかの XML コンテンツ] ``` #### プリフライトリクエストとリダイレクト 現在のところ、すべてのブラウザーが下記のようなプリフライトリクエストのリダイレクトに対応しているわけではありません。プリフライトリクエストにリダイレクトが発生すると、ブラウザーによっては以下のようなエラーメッセージを報告します。 -> The request was redirected to 'https\://example.com/foo', which is disallowed for cross-origin requests that require preflight. +> The request was redirected to `https://example.com/foo`, which is disallowed for cross-origin requests that require preflight. > Request requires preflight, which is disallowed to follow cross-origin redirects. もともと CORS プロトコルはそのような動作を要求していましたが、[その後で必要がないと変更されました](https://github.com/whatwg/fetch/commit/0d9a4db8bc02251cc9e391543bb3c1322fb882f2)。しかし、多くのブラウザーはまだ変更を実装していないため、もともと要求されていた動作に従っています。 @@ -275,27 +280,26 @@ Content-Type: text/plain > [!NOTE] > 資格情報を含むリクエストを異なるドメインに行う場合、サードパーティクッキーポリシーも適用されます。このポリシーは、この章で説明しているように、サーバーやクライアントでの設定とは無関係に常に実施されます。 -{{domxref("XMLHttpRequest")}} や [Fetch](/ja/docs/Web/API/Fetch_API) と CORS の両方によって明らかになる最も興味深い機能は、[HTTP クッキー](/ja/docs/Web/HTTP/Cookies)と HTTP 資格情報によってわかる「資格情報を含む」リクエストを作成することができることです。既定では、オリジン間の `XMLHttpRequest` または [Fetch](/ja/docs/Web/API/Fetch_API) の呼び出しにおいて、ブラウザーは資格情報を送信**しません**。 `XMLHttpRequest` オブジェクトまたは {{domxref("Request")}} のコンストラクターの呼び出し時に、特定のフラグを設定する必要があります。 +{{domxref("Window/fetch", "fetch()")}} または {{domxref("XMLHttpRequest")}} と CORS の両方によって明らかになる最も興味深い機能は、[HTTP クッキー](/ja/docs/Web/HTTP/Cookies)と HTTP 資格情報によってわかる「資格情報を含む」リクエストを作成することができることです。既定では、オリジン間の `fetch()` または `XMLHttpRequest` の呼び出しにおいて、ブラウザーは資格情報を送信**しません**。 + +`fetch()` リクエストに資格情報を含めるようにするには、[`credentials`](/ja/docs/Web/API/RequestInit#credentials) オプションを `"include"` に設定してください。 + +`XMLHttpRequest` リクエストに資格情報を含めるようにするには、{{domxref("XMLHttpRequest.withCredentials")}} プロパティを `true` に設定してください。 以下の例では `https://foo.example` から読み込まれた元のコンテンツが、 `https://bar.other` にあるリソースに対してクッキーを設定したシンプルな GET リクエストを行います。 foo.example のコンテンツは以下のような JavaScript を含んでいるかもしれません。 ```js -const invocation = new XMLHttpRequest(); const url = "https://bar.other/resources/credentialed-content/"; -function callOtherDomain() { - if (invocation) { - invocation.open("GET", url, true); - invocation.withCredentials = true; - invocation.onreadystatechange = handler; - invocation.send(); - } -} +const request = new Request(url, { credentials: "include" }); + +const fetchPromise = fetch(request); +fetchPromise.then((response) => console.log(response)); ``` -7 行目で、クッキー付きで呼び出しを行うために {{domxref("XMLHttpRequest")}} に設定する必要があるフラグ、 `withCredentials` という論理型の値を示しています。既定では、クッキーなしで呼び出しが行われます。これは単純な `GET` リクエストなのでプリフライトは行いませんが、ブラウザーは {{HTTPHeader("Access-Control-Allow-Credentials")}}`: true` ヘッダーを持たないレスポンスを**拒否**し、ウェブコンテンツを呼び出すレスポンスを作成**しない**でしょう。 +このコードは、{{domxref("Request")}} オブジェクトを作成し、コンストラクターで `credentials` オプションを `"include"` に設定し、このリクエストを `fetch()` に渡します。これは単純な `GET` リクエストなのでプリフライトは行いませんが、ブラウザーは {{HTTPHeader("Access-Control-Allow-Credentials")}}`: true` ヘッダーを持たないレスポンスを**拒否**し、ウェブコンテンツを呼び出すレスポンスを作成**しない**でしょう。 -![Access-Control-Allow-Credentials を使用した単純な GET リクエストの図。](cred-req-updated.png) +![Access-Control-Allow-Credentials を使用した単純な GET リクエストの図。](include-credentials.svg) 以下はクライアントとサーバーとの間のやりとりの例です。 @@ -326,10 +330,10 @@ Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Content-Type: text/plain -[text/plain payload] +[text/plain コンテンツ] ``` -10 行目に `https://bar.other` 向けのクッキーが含まれていますが、bar.other が {{HTTPHeader("Access-Control-Allow-Credentials")}}`: true` (16 行目) をレスポンスに含めなければ、レスポンスは無視されこのウェブコンテンツで使用できません。 +リクエストの `Cookie` ヘッダーには、`https://bar.other` のコンテンツを対象とした Cookie が含まれていますが、この例で示されているように、bar.other が {{HTTPHeader("Access-Control-Allow-Credentials")}}`: true` を返さなかった場合、レスポンスは無視され、ウェブコンテンツには利用できません。 #### プリフライトリクエストと資格情報 @@ -338,31 +342,30 @@ CORS のプリフライトリクエストに資格情報を含めてはいけま > [!NOTE] > エンタープライズ認証サービスの中には、 [Fetch](https://fetch.spec.whatwg.org/#cors-protocol-and-credentials) 仕様書に反して、プリフライトリクエストで TLS クライアント証明書を送信することを要求するものがあります。 > -> Firefox 87 では、`network.cors_preflight.allow_client_cert` を `true` に設定することで、この準拠していない動作を有効にすることができます。([Firefox バグ 1511151](https://bugzil.la/1511151)). Chromium ベースのブラウザーでは現在、CORS プリフライトリクエストで TLS クライアント証明書を常に送信します ([Chrome bug 775438](https://bugs.chromium.org/p/chromium/issues/detail?id=775438))。 +> Firefox 87 では、`network.cors_preflight.allow_client_cert` を `true` に設定することで、この準拠していない動作を有効にすることができます。([Firefox バグ 1511151](https://bugzil.la/1511151)). Chromium ベースのブラウザーでは現在、CORS プリフライトリクエストで TLS クライアント証明書を常に送信します ([Chrome バグ 775438](https://crbug.com/775438))。 #### 資格情報付きリクエストとワイルドカード 資格情報付きリクエストに返答する場合、 -- サーバーは `Access-Control-Allow-Origin` ヘッダーで "`*`" ワイルドカードを指定**してはならず**、 `Access-Control-Allow-Origin: https://example.com` のように、明示的にオリジンを指定しなければなりません。 - -- サーバーは `Access-Control-Allow-Headers` ヘッダーで "`*`" ワイルドカードを指定**してはならず**、 `Access-Control-Allow-Headers: X-PINGOTHER, Content-Type` のように、明示的にヘッダー名を指定しなければなりません。 - -- サーバーは `Access-Control-Allow-Methods` ヘッダーで "`*`" ワイルドカードを指定**してはならず**、 `Access-Control-Allow-Methods: POST, GET` のように、明示的にメソッド名を指定しなければなりません。 +- サーバーは `Access-Control-Allow-Origin` ヘッダーで `*` ワイルドカードを指定**してはならず**、 `Access-Control-Allow-Origin: https://example.com` のように、明示的にオリジンを指定しなければなりません。 +- サーバーは `Access-Control-Allow-Headers` ヘッダーで `*` ワイルドカードを指定**してはならず**、 `Access-Control-Allow-Headers: X-PINGOTHER, Content-Type` のように、明示的にヘッダー名を指定しなければなりません。 +- サーバーは `Access-Control-Allow-Methods` ヘッダーで `*` ワイルドカードを指定**してはならず**、 `Access-Control-Allow-Methods: POST, GET` のように、明示的にメソッド名を指定しなければなりません。 +- サーバーは `Access-Control-Expose-Headers` ヘッダーで `*` ワイルドカードを指定**してはならず**、 `Access-Control-Expose-Headers: Content-Encoding, Kuma-Revision` のように、明示的にメソッド名を指定しなければなりません。 リクエストが資格情報 (多くの場合は `Cookie` ヘッダー) を含んでおり、そのレスポンスが `Access-Control-Allow-Origin: *` ヘッダー (つまりワイルドカード) を含んでいる場合、ブラウザーはレスポンスへのアクセスをブロックし、開発ツールのコンソールに CORS エラーを報告します。 ただし、リクエストが (`Cookie` ヘッダーのような) 資格情報を含んで行われ、そのレスポンスがワイルドカードではない実際のオリジンを含んでいる場合 (例えば `Access-Control-Allow-Origin: https://example.com` など)、ブラウザーは指定されたオリジンからのレスポンスへのアクセスを許可します。 -また、レスポンス内の `Access-Control-Allow-Origin` レスポンスヘッダーの値が実際のオリジンではなく "`*`" ワイルドカードであった場合、クッキーは設定されません。 +また、レスポンス内の `Access-Control-Allow-Origin` レスポンスヘッダーの値が実際のオリジンではなく `*` ワイルドカードであった場合、クッキーは設定されません。 #### サードパーティークッキー -CORS のレスポンスに設定されたクッキーは、サードパーティークッキーに関する通常のポリシーに従うことに注意してください。上記の例では、ページは `foo.example` から読み込まれていますが、 19 行目のクッキーは `bar.other` から送られているので、ユーザーのブラウザーがサードパーティークッキーをすべて拒否するよう設定されていた場合は保存されません。 +CORS のレスポンスに設定されたクッキーは、サードパーティークッキーに関する通常のポリシーに従うことに注意してください。上記の例では、ページは `foo.example` から読み込まれていますが、レスポンスの `Cookie` ヘッダーは `bar.other` から送られているので、ユーザーのブラウザーがサードパーティークッキーをすべて拒否するよう設定されていた場合は保存されません。 リクエスト中のクッキー (10 行目) は、通常のサードパーティクッキーポリシーでも抑制されることがあります。したがって、クッキーポリシーが強制されていると、この章で説明されている機能が無効になり、事実上、認証されたリクエストを行うことができなくなるかもしれません。 -[SameSite](/ja/docs/Web/HTTP/Headers/Set-Cookie/SameSite) 属性に関するクッキーポリシーは適用されます。 +[SameSite](/ja/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value) 属性に関するクッキーポリシーは適用されます。 ## HTTP レスポンスヘッダー @@ -376,7 +379,7 @@ CORS のレスポンスに設定されたクッキーは、サードパーティ Access-Control-Allow-Origin: | * ``` -`Access-Control-Allow-Origin` は、リソースへのアクセスを許可するオリジンをブラウザーに伝えるための単一のオリジン、または — 資格情報を**含まない**リクエストにおいては — どのオリジンにもリソースへのアクセスを許可することをブラウザーに伝えるワイルドカード "`*`" のどちらかを指定することができます。 +`Access-Control-Allow-Origin` は、リソースへのアクセスを許可するオリジンをブラウザーに伝えるための単一のオリジン、または — 資格情報を**含まない**リクエストにおいては — どのオリジンにもリソースへのアクセスを許可することをブラウザーに伝えるワイルドカード `*` のどちらかを指定することができます。 例えば、 `https://mozilla.org` のオリジンからのコードにリソースへのアクセスを許可するには、次のように指定します。 @@ -385,11 +388,11 @@ Access-Control-Allow-Origin: https://mozilla.org Vary: Origin ``` -サーバーがワイルドカード "`*`" ではなく (ホワイトリストの一部としてリクエストするオリジンに基づいて動的に変更される可能性がある) 単一のオリジンを指定した場合は、サーバーは `Origin` を {{HTTPHeader("Vary")}} レスポンスヘッダーに含めて、サーバーのレスポンスが {{HTTPHeader("Origin")}} リクエストヘッダーの値によって変化することもクライアントに示してください。 +サーバーがワイルドカード `*` ではなく (ホワイトリストの一部としてリクエストするオリジンに基づいて動的に変更される可能性がある) 単一のオリジンを指定した場合は、サーバーは `Origin` を {{HTTPHeader("Vary")}} レスポンスヘッダーに含めて、サーバーのレスポンスが {{HTTPHeader("Origin")}} リクエストヘッダーの値によって変化することもクライアントに示してください。 ### Access-Control-Expose-Headers -{{HTTPHeader("Access-Control-Expose-Headers")}} ヘッダーは、指定されたヘッダーをブラウザー内の JavaScript ({{domxref("XMLHttpRequest.getResponseHeader()","getResponseHeader()")}} など) からアクセスできる許可リストに加えます。 +{{HTTPHeader("Access-Control-Expose-Headers")}} ヘッダーは、指定されたヘッダーをブラウザー内の JavaScript({{domxref("Response.headers")}} など)からアクセスできる許可リストに加えます。 ```http Access-Control-Expose-Headers: [, ]* @@ -443,7 +446,7 @@ Access-Control-Allow-Headers: [, ]* ## HTTP リクエストヘッダー -この節では、 HTTP リクエストを発行する際、オリジン間リソース共有機能を利用するためにクライアントが使用できるヘッダーの一覧を掲載します。なお、これらのヘッダーはサーバーの呼び出し時に設定されます。オリジン間 {{domxref("XMLHttpRequest")}} の機能を使用する開発者は、オリジン間リソース共有のリクエストヘッダーをプログラムで設定する必要はありません。 +この節では、 HTTP リクエストを発行する際、オリジン間リソース共有機能を利用するためにクライアントが使用できるヘッダーの一覧を掲載します。なお、これらのヘッダーはサーバーの呼び出し時に設定されます。オリジン間リクエストを作成する開発者は、オリジン間共有リクエストヘッダーをプログラムで設定する必要はありません。 ### Origin @@ -471,10 +474,10 @@ Access-Control-Request-Method: ### Access-Control-Request-Headers -{{HTTPHeader("Access-Control-Request-Headers")}} ヘッダーは、プリフライトリクエストを発行する際に、実際のリクエストを行う際に ({{domxref("XMLHttpRequest.setRequestHeader()","setRequestHeader()")}} などによって) 使用する HTTP ヘッダーをサーバーに知らせるために使用します。このブラウザー側のヘッダーは、サーバー側のヘッダー {{HTTPHeader("Access-Control-Allow-Headers")}} によって回答されます。 +{{HTTPHeader("Access-Control-Request-Headers")}} ヘッダーは、プリフライトリクエストを発行する際に、実際のリクエストを行う際に(例えば、[`headers`](/ja/docs/Web/API/RequestInit#headers) オプションとして渡すことで)使用する HTTP ヘッダーをサーバーに知らせるために使用します。このブラウザー側のヘッダーは、サーバー側のヘッダー {{HTTPHeader("Access-Control-Allow-Headers")}} によって回答されます。 -``` -Access-Control-Request-Headers: [, ]* +```http +Access-Control-Request-Headers: [,]* ``` 使用例は[前述のとおりです](#プリフライトリクエスト)。 @@ -490,13 +493,13 @@ Access-Control-Request-Headers: [, ]* ## 関連情報 - [CORS のエラー](/ja/docs/Web/HTTP/CORS/Errors) -- [Enable CORS: I want to add CORS support to my server](https://enable-cors.org/server.html) +- [CORS の有効化: CORS 対応をサーバーに追加したい](https://enable-cors.org/server.html)(英語) +- [フェッチ API](/ja/docs/Web/API/Fetch_API) - {{domxref("XMLHttpRequest")}} -- [Fetch API](/ja/docs/Web/API/Fetch_API) -- [Will it CORS?](https://httptoolkit.tech/will-it-cors) - 対話型の CORS の説明および生成 -- [How to run Chrome browser without CORS](https://alfilatov.com/posts/run-chrome-without-cors/) -- [Using CORS with All (Modern) Browsers](https://www.telerik.com/blogs/using-cors-with-all-modern-browsers) -- [Stack Overflow のよくある問題を解決するための "how to" 情報](https://stackoverflow.com/questions/43871637/no-access-control-allow-origin-header-is-present-on-the-requested-resource-whe/43881141#43881141): +- [Will it CORS?](https://httptoolkit.com/will-it-cors/) - 対話型の CORS の説明および生成(英語) +- [Chrome ブラウザーを CORS なしで実行する方法](https://alfilatov.com/posts/run-chrome-without-cors/)(英語) +- [すべての(現代の)ブラウザーで CORS を使用](https://www.telerik.com/blogs/using-cors-with-all-modern-browsers)(英語) +- [Stack Overflow のよくある問題を解決するための "how to" 情報](https://stackoverflow.com/questions/43871637/no-access-control-allow-origin-header-is-present-on-the-requested-resource-whe/43881141#43881141)(英語): - CORS のプリフライトを防止する方法 - CORS プロキシーを使用して「Access-Control-Allow-Origin ヘッダーの欠落」を回避する方法 diff --git a/files/ja/web/http/cors/preflight-correct.svg b/files/ja/web/http/cors/preflight-correct.svg new file mode 100644 index 00000000000000..fc5c47cba018fd --- /dev/null +++ b/files/ja/web/http/cors/preflight-correct.svg @@ -0,0 +1 @@ +サーバークライアントサーバークライアントプリフライトリクエストメインリクエストOPTIONS /doc HTTP/1.1Origin: https://foo.exampleAccess-Control-Request-Method: POSTAccess-Control-Request-Headers: X-PINGOTHER, Content-type...HTTP/1.1 204 No ContentAccess-Control-Allow-Origin: https://foo.exampleAccess-Control-Allow-Methods: POST, GET, OPTIONSAccess-Control-Allow-Headers: X-PINGOTHER, Content-typeAccess-Control-Max-Age: 86400...POST /doc HTTP/1.1X-PINGOTHER: pingpongContent-Type: text/xml; charset=UTF8Origin: https://foo.example...HTTP/1.1 200 OKAccess-Control-Allow-Origin: https://foo.exampleVary: Accept-Encoding, OriginContent-Length: 235... \ No newline at end of file diff --git a/files/ja/web/http/cors/preflight_correct.png b/files/ja/web/http/cors/preflight_correct.png deleted file mode 100644 index 72f9a8bf89bcd1..00000000000000 Binary files a/files/ja/web/http/cors/preflight_correct.png and /dev/null differ diff --git a/files/ja/web/http/cors/simple-req.png b/files/ja/web/http/cors/simple-req.png deleted file mode 100644 index 8c3f857e7c892f..00000000000000 Binary files a/files/ja/web/http/cors/simple-req.png and /dev/null differ diff --git a/files/ja/web/http/cors/simple-request.svg b/files/ja/web/http/cors/simple-request.svg new file mode 100644 index 00000000000000..1e7e83042e327d --- /dev/null +++ b/files/ja/web/http/cors/simple-request.svg @@ -0,0 +1,63 @@ + + + + サーバー + + + + クライアント + + + + + + サーバー + + + + + + + クライアント + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GET /resources/public-data/ HTTP/1.1 + Origin: https://foo.example + + HTTP/1.1 200 OK + Access-Control-Allow-Origin: * + +