クロスオリジンリソース共有 (CORS)

ガイド ベストプラクティス クロスオリジンリソース共有 (CORS)

クロスオリジンリソース共有 (CORS)

クロスオリジンリソース共有 (CORS) は、悪意のあるウェブサイトが明示的な権限を持たずに他のサイトのデータ (Box APIなど) にアクセスするのを防ぐために、ウェブブラウザで利用されているセキュリティメカニズムです。

CORSは、ウェブブラウザを使用してウェブページから送信されるBox APIリクエストのみに適用され、そのブラウザによって渡されるHTTP Originヘッダーを利用します。サーバー側環境では機能しません。

CORSの全般的な情報の詳細については、MDN web docsを参照してください。

CORSのしくみ

あるドメイン (company.comなど) のブラウザで、別のドメイン (box.com) から画像、ファイル、またはAPIリソースを取得しようとする場合、適切なCORSヘッダーが存在しない限り、そのウェブブラウザにより、これらのアセットへのアクセスが阻止されます。

sequenceDiagram participant b as Browser (company.com) participant s1 as Server (company.com) participant s2 as Server (box.com) Note over b,s1: Same-origin request, always allowed b ->> s1: GET /data.json (Origin: https://company.com) s1 ->> b: JSON (No extra headers) Note over b,s2: Cross-origin request, controlled by CORS b ->> s2: GET /2.0/folders/0 (Origin: https://company.com) s2 ->> b: JSON (Access-Control-Allow-Origin: *)

ブラウザからクロスオリジンリクエストを送信すると、そのリクエストを送信するサイトのドメインを含むOriginリクエストヘッダーがリクエストとともに渡されます。このヘッダーは変更できないため、ウェブブラウザのセキュリティにとって重要な部分となります。

デフォルトでは、Access-Control-Allow-Origin応答ヘッダーが存在しない場合、ブラウザは、別のドメインから読み込まれたアセットを受け入れることはありません。Boxなどのサーバーは、そのサーバー上のリソースへのアクセスが許可されたドメインのリストを明示的に取得することも、APIへのアクセスを任意のドメインに許可するために*値を返すこともできます。

BoxでのCORSの使用方法

Boxは、OriginリクエストヘッダーとAccess-Control-Allow-Origin応答ヘッダーを使用して、開発者が定義したCORSルールを適用します。

Originヘッダーの検証

Box APIは、アプリケーション開発者が設定した、許可されたドメインのリストに対してOriginリクエストヘッダーを検証します。許可されたオリジンは複数設定でき、リストにないオリジンはHTTP 403エラーで返されます。

{
  "type":"error",
  "status":403,
  "code":"cors_origin_not_whitelisted",
  "context_info":{
    "origin":"https:\/\/company.com"
  },
  "help_url":"http:\/\/developers.box.com\/docs\/#errors",
  "message":"Access denied - Did you forget to safelist your origin in the CORS config of your app?",
  "request_id":"4dsdfsa832213"
}

オリジンが設定されていない場合は、このアプリケーションのBox APIに対するすべてのリクエストでエラーが返されます。

Access-Control-Allow-Origin応答ヘッダー

Box APIは、Originヘッダーを検証した後、リクエストされたデータのほか、値*が設定されたOrigin応答ヘッダーを返します。

HTTP/1.1 200 OK
Date: Wed, 23 Sep 2020 14:07:29 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
Strict-Transport-Security: max-age=31536000
Cache-Control: no-cache, no-store
Access-Control-Allow-Origin: *
Vary: Origin
BOX-REQUEST-ID: 032cfb446dae4fd0b4c2bff80a1a97ba7

このヘッダーを返すことにより、Box APIはウェブブラウザに対して、データをリクエストしたサイトで応答を使用できることを通知します。

ドメインのCORSの有効化

アプリケーションが動作するドメインでCORSを有効にするには、開発者コンソールに移動して、アプリケーションを選択し、[構成] パネルの一番の下までスクロールして [CORSドメイン] の設定を見つけます。

アプリケーションでのAPIリクエストの発信元になると予想されるすべてのオリジンをカンマ区切りリストとして追加します。ドメインにはスキーマ(httpまたはhttps)が必要で、*.example.comのようにサブドメインのワイルドカードを含めることができます。

サイトが非標準のポート上で動作する場合は、サイトにそのポートも含める必要があります。これは、特に、localhostまたは127.0.0.1で動作しているサイトに当てはまります。

オリジンのリストの例は、次のようになります。

https://company.com,https://*.internal.company.com,http://localhost:3000

CORSのデバッグ

Box APIに対してAPI呼び出しを実行した場合に発生する可能性のあるCORSエラーはいくつかあります。

HTTP 403 - 許可済みオリジンが定義されていない

オリジンのリストを指定した後でも、このエラーが発生する場合があります。多くの場合、指定したオリジンに誤字があることが原因です。

  1. オリジンを確認する - 開発者コンソールに戻り、オリジンがAPI呼び出しの実行元のサイトをマップしていることを確認します。オリジンにはスキーム (http(s)) が含まれても、パスまたは末尾の/は含まれないことに注意してください。ブラウザのデバッグコンソールを使用してページを調査し、Originリクエストヘッダーの値を確認することをお勧めします。この値は、開発者コンソールで指定された値のいずれかと一致する必要があります。
  2. 資格情報を確認する - このエラーのもう1つの理由として、オリジンを設定したアプリケーションとは別のアプリケーションとして認証している可能性が考えられます。資格情報が、使用するアプリケーションのものと一致することを確認してください。サーバー側スクリプトから呼び出しを実行して、API呼び出しが動作するかどうかを検証することをお勧めします。

Cross-Origin Request Blocked

場合によっては、CORSに言及するJavaScriptエラーが発生します。

Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at https://api.box.com/2.0/users/me. (Reason: CORS
request did not succeed).

多くの場合、このエラーはCORSとはほとんど関係がありません。代わりに、以下を確認することをお勧めします。

  1. 認証ヘッダーを確認する - 認証ヘッダーが指定されていないか、またはその形式が正しくない場合、このAPIでは、必要なAccess-Control-Allow-Originヘッダーなしで一般的なエラーが返されます。その結果、ブラウザで前述のエラーが発生します。必ずAuthorization: Bearer ...ヘッダーを使用してアクセストークンを渡してください。
  2. VPNやプロキシなどでブロックされたリクエストがないか確認する - 場合によっては、VPN、会社のプロキシ、ブラウザの機能拡張、DNSプロバイダ、またはネットワークトラフィックを妨害する可能性があるその他のサービスによってBox APIがブロックされている可能性があります。このようなサービスはどれも、リクエストをインターセプトし、必要なAccess-Control-Allow-Originヘッダーが含まれていないまったく新しいリクエストを返す場合があります。このケースをテストするには、ブラウザ以外の環境、シークレットウィンドウ、またはまったく別の (会社が所有していない) デバイスから同じAPI呼び出しを実行してみてください。