CORS と許可されたオリジン
公開可能キーには、そのキーを使用できるウェブサイトを指定する許可オリジンのリストを設定できます。このページでは、リストがどこで検証されるか、検証に失敗したときにブラウザーに何が表示されるか、またリストでは何を保護できないかを説明します。
オリジンは、ページのスキーム、ホスト、ポートで構成されます。例は https://www.example.com や http://localhost:3000 です。パスは含みません。
リストの設定は任意です
公開可能キーはリストが空でも機能します。**空のリストは、どのオリジンでもそのキーを使用できることを意味します。**オリジンを指定せずに作成したキーはこの状態になり、後からリストを空にすることもできます。
本番サイトにキーを配置する前に、オリジンを登録してください。リストを設定すると、キーのコピーを他人のウェブページに貼り付けても、そのページでは機能しません。この制限で保護できることとできないことについては、以下で説明します。
サーバーキーには、このリストは適用されません。リストの内容にかかわらず、サーバーキーをブラウザーから使用することはできません。
エントリの記述方法
| ルール | 例 |
|---|---|
| スキーム、ホスト、必要な場合のポートのみを指定します。パス、クエリ、末尾のスラッシュは含めません。 | https://www.example.com。https://www.example.com/ は不可 |
https が必要です。http は localhost、127.0.0.1、[::1] に限って使用できます。API キーのページでは [::1] を受け付けないため、キー管理 API で設定してください。 | http://localhost:3000 |
完全一致で照合されます。ワイルドカードは使用できず、サブドメインは別のオリジンです。* を含むエントリはそのまま保存されますが、どのサイトとも一致しません。 | https://example.com と https://www.example.com は別々のエントリです |
ポートもオリジンの一部です。既定のポート(https の 443、http の 80)は省略します。 | https://example.com。https://example.com:443 は不可 |
| キーごとに最大 100 件まで登録できます。重複するエントリは拒否されます。 |
エントリは小文字に変換され、既定のポート が除去された状態で保存されるため、ブラウザーが送信する値と一致します。
ディスクから直接開いたページ(file://)には、登録可能なオリジンがありません。開発中は http://localhost からページを配信してください。
リストは kaleidr.com/api-keys の API キーページ、またはキー管理 API の allowed_origins で設定します。
リストを検証する場所
| ステップ | 検証内容 | 失敗した場合 |
|---|---|---|
| 1. セッション交換 | キーにリストが設定されている場合、リクエストの Origin ヘッダーがリストに含まれている必要があります。 | 403 origin_not_allowed。セッションは発行されません。Origin ヘッダー自体がないリクエストは 403 origin_required になります。 |
| 2. すべての実行時呼び出し | セッショントークンは、発行時に関連付けられたオリジンからのみ使用できます。 | 403 session_origin_mismatch。 |
| 3. すべての実行時呼び出し | セッションのオリジンが、引き続きキーのリストに含まれている必要があります。 | 401。キーからオリジンを削除すると、そのオリジンに対して発行済みのセッションも終 了します。 |
| 4. キーを含むベースマップのリクエスト | 埋め込み Editor は、スタイル、スプライト、フォントの取得時に、公開可能キーを https://tile.kaleidr.com に送信します。キーを認識させるには、ページのサイトがキーのリストに含まれている必要があります。 | リクエストはキーがないものとして扱われ、429 tile_access_restricted で拒否されることがあります。 |
| 5. レスポンス | 資格情報が有効で、オリジンが許可されている場合に限り、API は Access-Control-Allow-Origin を追加します。 | ブラウザーがページによるレスポンスの読み取りをブロックします。 |
ステップ 5 は通常の CORS の動作であり、ブラウザー上でエラーがこのように見える理由です。
プリフライト
JSON 本文を含むクロスサイトの POST リクエストを送信する前に、ブラウザーは OPTIONS プリフライトリクエストを送ります。プリフライトには資格情報が含まれず、キーと照合できないため、Kaleidr はすべてのプリフライトに 204 で応答し、要求元のオリジンを返します。リストが適用されるのは実際のリクエストです。
| プリフライトレスポンスヘッダー | 値 |
|---|---|
Access-Control-Allow-Methods | GET, POST, OPTIONS |
Access-Control-Allow-Headers | Authorization, Content-Type, Accept, X-Api-Key, X-Request-Id |
Access-Control-Max-Age | 600 |
それ以外のヘッダーを送信するリクエストは、ブラウザーのプリフライトで失敗します。ページから行う呼び出しにカスタムヘッダーを追加しないでください。
許可されたレスポンスでは、API は Access-Control-Expose-Headers: Retry-After, X-Request-Id も送信するため、ページのコードからこの 2 つのヘッダーを読み取れます。API は Cookie を使用しないため、Access-Control-Allow-Credentials は一切送信しません。
オリジンが拒否されたときの表示
オリジンまたは資格情報が拒否されると、レスポンスに Access-Control-Allow-Origin ヘッダーは付きません。そのためブラウザーはレスポンスをコードから隠します。ページには 401 や 403 のレスポンス本文は表示されず、ネットワークエラーが発生します。
コンソールには次のように表示されます。
Access to fetch at 'https://api.kaleidr.com/inference-api/b2b/v1/sdk/sessions' from origin
'https://www.example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present
on the requested resource.
その後、[kaleidr:chat](またはマウントした製品名)を含む SDK のエラーが続きます。何も描画されません。
実際の原因を確認するには、ブラウザーの Network タブを開き、sessions リクエストを選択して、ステータスとレスポンスを確認してください。
| Network タブのステータスとコード | 原因 | 対処方法 |
|---|---|---|
403 origin_not_allowed | このページのオリジンがキーのリストにありません。 | リクエストの Origin ヘッダーに表示されるオリジンを正確に追加します。 |
401 Invalid or missing API key | キーの入力間違い、失効、または期限切れです。 | API キーのページでキーを確認します。 |
403 server_key_in_browser | サーバーキーがページから送信されました。 | ページには公開可能キーを使用し、サーバーキーをローテーションします。 |
403 session_origin_mismatch | セッションを要求したオリジンと異なるオリジンで、セッショントークンが使用されました。 | 実際に使用するページ上でセッション交換を行います。 |
プリフライトが失敗し、POST が表示されない | リクエストが許可リストにないヘッダーを送信しました。 | カスタムヘッダーを削除します。 |
資格情報とオリジンが受け入れられた後は、以降のエラーをページから読み取ることができ、CORS エラーにはなりません。オリジンでは有効でも対象の製品には許可されていないキーの場合、セッション交換は読み取り可能な 403(product_not_allowed、insufficient_scope、tier_capability_not_allowed のいずれか)で失敗し、SDK は kaleidr session exchange failed (403) を記録します。有効なセッションに対する 429 は、レスポンス本文と Retry-After ヘッダーを伴って返されます。各コードについては認証とスコープを参照してください。
CORS はブラウザーの規則なので、同じリクエストをターミナルから実行しても CORS エラーは発生しません。このため、curl でキーとオリジンの組み合わせを素早く確認できます。
curl -i https://api.kaleidr.com/inference-api/b2b/v1/sdk/sessions \
-H "X-Api-Key: kld_pk_live_…" \
-H "Origin: https://www.example.com" \
-H "Content-Type: application/json" \
-d '{"product":"chat"}'
許可されたオリジンには access-control-allow-origin: https://www.example.com を含む 200 が返されます。拒否されたオリジンには、このヘッダーのない 403 {"detail":"origin_not_allowed"} が返されます。
ブラウザーからのサーバーキー使用が拒否される理由
サーバーキーは継続的に有効な秘密情報です。そのキーを入手した人は、失効するまで、あなたの組織として API を呼び出せます。ページのソースは公開されているため、ページ内に置かれたサーバーキーは漏えいしたキーと見なされます。
Kaleidr は次の 3 段階でサーバーキーを拒否します。
- SDK はマウント時に
kld_sk_…キーを拒否し、ネットワークリクエストを送信せずにコンソールエラーを表示します。 Originヘッダーを含むサーバーキーのリクエストには、API が 403server_key_in_browserを返します。- レスポンスに CORS ヘッダーがないため、いずれにしてもページのコードはレスポンスを読み取れません。
これらの対策によって漏えい自体が取り消されるわけではありません。CORS がブロックするのはレスポンスであり、リクエスト ではありません。API がリクエストを拒否するまでにキーは送信済みで、ページを確認した人なら誰でも見られます。サーバーキーをページに含めたことがある場合は、キーをローテーションしてください。
このリストは秘密情報を守る仕組みではありません
ブラウザーは Origin ヘッダーを正しく設定しますが、ブラウザー以外にはその制約がありません。スクリプト、コマンドラインツール、サーバーは、前述の curl の例と同様に、任意の Origin を送信できます。つまり、次のようになります。
- **このリストは他のウェブサイトによる利用を防ぎます。**他のサイトの訪問者のブラウザーであなたのキーが使われることを防ぐのが役割で、その目的には有効です。
- **このリストは、意図的な呼び出し元までは防げません。**公開可能キーをコピーし、ブラウザー以外からあなたのオリジンを指定してリクエストを送信する相手は防げません。
この点を考慮して設計してください。公開可能キーでアクセスできるものはすべて、あなたのページを閲覧できる人にも利用可能だと考えてください。
- キーごとの月間上限とレート制限で、クォータの消費リスクを抑えてください。クォータとレート制限を参照してください。
- 公開可能キーに
vendorスコープを設定するのは、そのキーで読めるデータを当該ページに公開してもよい場合に限ってください。認証とスコープを参照してください。 - サイトごとにキーを分け、一つのキーを失効させても他のサイトに影響しないようにしてください。
詳細はセキュリティモデルを参照してください。
URL に含まれる資格情報
プラットフォーム API の呼び出しでは、資格情報は必ずヘッダーに含まれ、URL には含まれません。Kaleidr の埋め込み機能によるベースマップのリクエストだけは例外で、リクエスト URL にブラウザー用資格情報が含まれます。この値はブラウザーの履歴やネットワークログに残る可能性があります。すでに公開情報である公開可能な資格情報なら許容されますが、サーバーキーでは決して許容されません。いかなる URL にも kld_sk_… を含めないでください。
リストの変更
- オリジンの追加は、そのオリジンから次にセッションを交換するときに反映されます。ベースマップのリクエストへの反映には数分か かることがあります。
- オリジンの削除により、そのオリジンからの新しい交換は拒否され、既存のセッションは次のリクエストで終了します。
- リストを空にすると、再び「すべてのオリジン」を許可する状態になります。
関連ページ
- 認証とスコープ:セッション交換と、すべての 401 および 403 エラー。
- セキュリティモデル:資格情報が漏えいした場合に可能になる操作。
- コンテンツセキュリティポリシー:自分のページで許可すべきホスト。
- トラブルシューティング:症状別の問題。