コンテンツにスキップ

CORS

クロスオリジンリソースシェアリング(CORS)は、HTTPヘッダーを使用して、あるオリジンで実行されているWebアプリケーションが別のオリジンの選択されたリソースにアクセスする許可を与えるメカニズムです。Webアプリケーションは、自身のオリジンとは異なるオリジン(ドメイン、プロトコル、ポートを含む)のリソースを要求する際に、クロスオリジンHTTPリクエストを実行します。

CORSリクエストがAccessで保護されたサイトに到達するためには、リクエストに有効なCF-Authorizationクッキーを含める必要があります。これはリクエストの種類に応じて追加の設定が必要になる場合があります。

シンプルリクエストを許可する

Accessで保護されたドメインにシンプルなCORSリクエストを行い、まだログインしていない場合、リクエストはCORSエラーを返します。このエラーを解決する方法は2つあります。

手動で認証する

  1. ブラウザでターゲットドメインにアクセスします。Accessのログインページが表示されます。
  2. ターゲットドメインにログインします。これにより、CF-Authorizationクッキーが生成されます。
  3. CORSリクエストを行ったページを更新します。更新により、新しく生成されたクッキーでリクエストが再送信されます。

事前確認リクエストを許可する

Accessで保護されたドメインに事前確認のクロスオリジンリクエストを行うと、OPTIONSリクエストは403エラーを返します。このエラーは、ドメインにログインしているかどうかに関係なく発生します。これは、ブラウザがOPTIONSリクエストにクッキーを含めないためです。したがって、Cloudflareは事前確認リクエストをブロックし、CORS交換が失敗します。

このエラーを解決する方法は3つあります。

オリジンへのOPTIONSリクエストをバイパスする

Cloudflareを設定して、OPTIONSリクエストを直接オリジンサーバーに送信できます。OPTIONSリクエストのためにAccessをバイパスするには:

  1. Zero Trustに移動し、Access > Applicationsに進みます。
  2. OPTIONSリクエストを受信するオリジンを見つけて、Editを選択します。
  3. Settingsタブで、CORS settingsまでスクロールします。
  4. Bypass options requests to originをオンにします。これにより、このアプリケーションの既存のCORS設定がすべて削除されます。

Access JWTのためにCORSを強制することは依然として重要です。このオプションは、オリジンサーバーでCORSの強制が確立されている場合にのみ使用するべきです。

事前確認リクエストへの応答を設定する

Cloudflareを設定して、あなたの代わりにOPTIONSリクエストに応答させることができます。OPTIONSリクエストはオリジンに到達しません。事前確認の交換が解決された後、ブラウザは認証クッキーを含むメインリクエストを送信します(Accessで保護されたドメインにログインしている場合)。

Cloudflareが事前確認リクエストにどのように応答するかを設定するには:

  1. Zero Trustに移動し、Access > Applicationsに進みます。

  2. OPTIONSリクエストを受信するオリジンを見つけて、Editを選択します。

  3. Settingsタブで、CORS settingsまでスクロールします。

  4. ダッシュボードのCORS設定を、オリジンから送信される応答ヘッダーに一致するように設定します。

    たとえば、api.mysite.comが次のヘッダーを返すように設定されている場合:

    headers: {
    'Access-Control-Allow-Origin': 'https://example.com',
    'Access-Control-Allow-Credentials' : true,
    'Access-Control-Allow-Methods': 'GET, OPTIONS',
    'Access-Control-Allow-Headers': 'office',
    'Content-Type': 'application/json',
    }

    その場合、Accessでapi.mysite.comに移動し、Access-Control-Allow-OriginAccess-Control-Allow-CredentialsAccess-Control-Allow-Methods、およびAccess-Control-Allow-Headersを設定します。 Zero TrustでのCORS設定の例

  5. Save applicationを選択します。

  6. (オプション)curlを使用してオリジンにOPTIONSリクエストを送信することで、設定を確認できます。たとえば、

    Terminal window
    curl --head --request OPTIONS https://api.mysite.com \
    --header 'origin: https://example.com' \
    --header 'access-control-request-method: GET'

    は、次のような応答を返すべきです:

    HTTP/2 200
    date: Tue, 24 May 2022 21:51:21 GMT
    vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers
    access-control-allow-origin: https://example.com
    access-control-allow-methods: GET
    access-control-allow-credentials: true
    expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
    report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=A%2FbOOWJio%2B%2FjuJv5NC%2FE3%2Bo1zBl2UdjzJssw8gJLC4lE1lzIUPQKqJoLRTaVtFd21JK1d4g%2BnlEGNpx0mGtsR6jerNfr2H5mlQdO6u2RdOaJ6n%2F%2BS%2BF9%2Fa12UromVLcHsSA5Y%2Fj72tM%3D"}],"group":"cf-nel","max_age":604800}
    nel: {"success_fraction":0.01,"report_to":"cf-nel","max_age":604800}
    server: cloudflare
    cf-ray: 7109408e6b84efe4-EWR

Cloudflare Workerで認証トークンを送信する

Cloudflare Accessで保護された2つのサイト、example.comapi.mysite.comの間で行われるリクエストはCORSチェックの対象となります。example.comにログインしたユーザーには、example.com用のクッキーが発行されます。ユーザーのブラウザがapi.mysite.comをリクエストすると、Cloudflare Accessはapi.mysite.comに特有のクッキーを探します。ユーザーがすでにapi.mysite.comにログインしていない場合、リクエストは失敗します。

二重ログインを避けるために、認証情報をapi.mysite.comに自動的に送信するCloudflare Workerを作成できます。

前提条件

1. サービストークンを生成する

新しいAccessサービストークンを生成するためにこれらの手順に従ってください。Client IDClient Secretを安全な場所にコピーします。後のステップで使用します。

2. サービス認証ポリシーを追加する

  1. Zero Trustに移動し、Access > Applicationsに進みます。

  2. api.mysite.comアプリケーションを見つけて、Editを選択します。

  3. Policiesタブを選択します。

  4. 次のポリシーを追加します:

    ActionRule typeSelector
    Service AuthIncludeService Token

3. 新しいWorkerを作成する

ターミナルを開き、次のコマンドを実行します:

Terminal window
npm create cloudflare@latest -- authentication-worker

これにより、create-cloudflareパッケージのインストールが促され、セットアップが進められます。

For setup, select the following options:

  • For What would you like to start with?, choose Hello World example.
  • For Which template would you like to use?, choose Hello World Worker.
  • For Which language do you want to use?, choose JavaScript.
  • For Do you want to use git for version control?, choose Yes.
  • For Do you want to deploy your application?, choose No (we will be making some changes before deploying).

プロジェクトディレクトリに移動します。

Terminal window
cd authentication-worker

/src/index.jsを開き、既存のコードを削除して次の例を貼り付けます:

// あなたのAPIが存在するホスト名
const originalAPIHostname = "api.mysite.com";
export default {
async fetch(request) {
// ホストだけを変更します。リクエストがexample.com/api/nameで来た場合、新しいURLはapi.mysite.com/api/nameになります
const url = new URL(request.url);
url.hostname = originalAPIHostname;
// あなたのAPIがapi.mysite.com/anyname(パスに"api/"がない場合)にある場合、
// example.com/api/nameの"api/"部分を削除します
// url.pathname = url.pathname.substring(4)
// 最良のプラクティスは、元のリクエストを使用して新しいリクエストを構築することです
// すべての属性をクローンします。URLを適用するには、コンストラクタが必要です
// 一度Requestが構築されると、そのURLは不変です。
const newRequest = new Request(url.toString(), request);
newRequest.headers.set("cf-access-client-id", CF_ACCESS_CLIENT_ID);
newRequest.headers.set("cf-access-client-secret", CF_ACCESS_CLIENT_SECRET);
try {
const response = await fetch(newRequest);
// 応答をコピーします
const modifiedResponse = new Response(response.body, response);
// 既存のクッキーを上書きしないように、応答からset-cookieを削除します
modifiedResponse.headers.delete("set-cookie");
return modifiedResponse;
} catch (e) {
return new Response(JSON.stringify({ error: e.message }), {
status: 500,
});
}
},
};

次に、WorkerをCloudflareアカウントにデプロイします:

Terminal window
npx wrangler deploy

4. Workerを設定する

  1. Cloudflareダッシュボードにログインし、アカウントを選択してWorkers & Pagesに進みます。

  2. 新しく作成したWorkerを選択します。

  3. Triggersタブで、Routesに進み、example.com/api/*を追加します。Workerはexample.comのサブパスに配置され、クロスオリジンリクエストを避けます。

  4. Settingsタブで、Variablesを選択します。

  5. Environment Variablesの下に、次の秘密変数を追加します:

    • CF_ACCESS_CLIENT_ID = <service token Client ID>
    • CF_ACCESS_CLIENT_SECRET = <service token Client Secret>

Client IDとClient Secretは、サービストークンからコピーします。

  1. 各変数のEncryptオプションを有効にし、Saveを選択します。

5. HTTPリクエストURLを更新する

example.comアプリケーションを修正して、すべてのリクエストをexample.com/api/に送信するようにします。

HTTPリクエストは、2つの異なるAccessで保護されたドメイン間でシームレスに機能するはずです。ユーザーがexample.comにログインすると、ブラウザはapi.mysite.comではなくWorkerにリクエストを送信します。WorkerはリクエストヘッダーにAccessサービストークンを追加し、その後リクエストをapi.mysite.comに転送します。サービストークンがService Authポリシーに一致するため、ユーザーはもはやapi.mysite.comにログインする必要がありません。

トラブルシューティング

一般的に、CORSの問題をトラブルシューティングする際には、次の手順を推奨します:

  1. 問題を説明したHARファイルをキャプチャし、同時に記録されたJSコンソールログ出力を取得します。HARファイルだけでは、クロスオリジンの問題の理由を完全に把握することはできません。
  2. アプリケーションがすべてのfetchまたはXHRリクエストでcredentials: 'same-origin'を設定していることを確認します。
  3. スクリプトタグでcross-origin設定を使用している場合、これらは「use-credentials」に設定する必要があります。