跳到主要内容

CORS 与允许的来源

可发布密钥可以配置允许的来源列表,即允许使用该密钥的网站。本页介绍在哪些环节检查此列表、检查失败时浏览器会显示什么,以及此列表无法提供哪些保护。

来源由页面的协议、主机和端口组成,例如 https://www.example.com 或 http://localhost:3000。来源不包含路径。

此列表为可选配置​

可发布密钥在列表为空时仍可使用。**列表为空意味着任何来源都可以使用该密钥。**创建密钥时不指定来源,就会处于这种状态;以后也可以清空列表。

将密钥用于生产网站之前,请先添加允许的来源。配置列表后,即使有人把您的密钥复制到其他人的网页中,该网页也无法使用它。有关此限制的保护范围及其局限性,请参阅下文。

服务器密钥不受此列表影响。无论列表包含什么内容,服务器密钥都无法从浏览器中使用。

如何填写条目​

规则示例
只包含协议、主机以及可选的端口。不得包含路径、查询参数或末尾斜杠。https://www.example.com,而不是 https://www.example.com/
必须使用 https。只有 localhost、127.0.0.1 和 [::1] 可以使用 http。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. 携带密钥的底图请求嵌入式编辑器在向 https://tile.kaleidr.com 请求样式、精灵图和字体时会发送您的可发布密钥。页面所在网站必须位于密钥列表中,密钥才能被识别。请求将被视为未携带密钥,可能返回 429 tile_access_restricted。
5. 响应只有凭据有效且来源被允许时,API 才会添加 Access-Control-Allow-Origin。浏览器会阻止页面读取响应。

第 5 步属于正常的 CORS 机制,也是浏览器中出现此类错误表现的原因。

预检请求​

浏览器在发送带有 JSON 请求体的跨站 POST 之前,会先发送 OPTIONS 预检请求。Kaleidr 对每个预检请求都会返回 204,并回显请求来源,因为预检请求不携带凭据,无法根据密钥进行验证。来源列表是在实际请求阶段应用的。

预检响应标头值
Access-Control-Allow-MethodsGET, POST, OPTIONS
Access-Control-Allow-HeadersAuthorization, Content-Type, Accept, X-Api-Key, X-Request-Id
Access-Control-Max-Age600

如果请求发送了任何其他标头,浏览器中的预检就会失败。请勿在页面发出的调用中添加自定义标头。

对于被允许的响应,API 还会发送 Access-Control-Expose-Headers: Retry-After, X-Request-Id,以便页面代码读取这两个标头。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 请求,然后查看其状态和响应:

网络选项卡中的状态及代码原因解决方法
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"}'

允许的来源会返回 200,并包含 access-control-allow-origin: https://www.example.com。被拒绝的来源则返回 403 {"detail":"origin_not_allowed"},且不包含该标头。

为什么浏览器不允许使用服务器密钥​

服务器密钥是一项长期有效的秘密凭据。任何获取密钥的人都可以在其被撤销之前,以您的组织身份调用 API。网页源代码是公开的,因此把服务器密钥放进页面就等于泄露密钥。

Kaleidr 会在三个环节拒绝服务器密钥:

  1. SDK 在挂载时拒绝 kld_sk_… 密钥,在控制台输出错误,并且不会发送网络请求。
  2. 任何携带 Origin 标头的服务器密钥请求都会收到 API 返回的 403 server_key_in_browser。
  3. 响应不带 CORS 标头,因此页面代码无论如何都无法读取该响应。

这些措施都无法撤销已经发生的泄露。CORS 阻止的是读取响应,而不是发送请求:在 API 拒绝请求之前,密钥就已经发送出去,查看页面的人也能够看到它。如果服务器密钥曾出现在网页中,请轮换该密钥。

来源列表并不能保护秘密​

浏览器会如实设置 Origin 标头,但其他客户端不受此约束。脚本、命令行工具或服务器可以任意设置 Origin,如上面的 curl 示例所示。因此:

  • 此列表可以阻止其他网站在其访问者的浏览器中使用您的密钥。这是列表的用途,也确实有效。
  • 此列表无法阻止有意绕过限制的调用者复制您的可发布密钥,并在浏览器之外伪装成您的来源发送请求。

请据此设计安全策略。凡是可发布密钥能够访问的内容,都应视为任何能查看您网页的人均可获取:

  • 使用单个密钥的每月额度上限及请求速率限制来控制配额风险。参阅配额与速率限制。
  • 除非您愿意在该网页上公开密钥可访问的数据,否则不要为可发布密钥赋予 vendor 权限范围。参阅身份验证与权限范围。
  • 每个网站使用独立密钥,以便撤销一个网站的密钥而不影响其他网站。

有关完整说明,请参阅安全模型。

URL 中的凭据​

平台 API 调用始终在标头中携带凭据,而不会放在 URL 中。Kaleidr 嵌入组件的底图请求是唯一例外:此类请求在 URL 中携带浏览器凭据,因此可能出现在浏览器历史记录和网络日志中。对于本来就公开的可发布凭据,这是可以接受的;但服务器密钥绝不能这样使用。切勿将 kld_sk_… 放入任何 URL。

修改列表​

  • 添加来源会在该来源下一次交换会话时生效。底图请求可能需要几分钟才能识别变更。
  • 移除来源会拒绝该来源发起的新会话交换,并在现有会话的下一次请求时使其失效。
  • 清空列表会恢复为“允许任何来源”。

相关文档​