所有文章 > 日积月累 > REST API 的 CORS(跨源资源共享)权威指南
REST API 的 CORS(跨源资源共享)权威指南

REST API 的 CORS(跨源资源共享)权威指南

有关 REST API 跨域资源共享 (CORS) 的深入指南,包括 CORS 的工作原理以及常见陷阱(尤其是在安全性方面)。

什么是 CORS?

CORS 是一种安全机制,允许来自一个域或源的网页访问具有不同域的资源(跨域请求)。 CORS 是现代浏览器中实现的同源策略的放宽。如果没有 CORS 之类的功能,网站就只能通过所谓的同源策略访问来自同一源的资源。

为什么存在同源?

和许多网站一样,您可能会使用 Cookie 来跟踪身份验证或会话信息。这些 Cookie 在创建时会绑定到某个域。每次对该域进行 HTTP 调用时,浏览器都会附加为该域创建的 Cookie。每次HTTP 调用都是如此,可能是针对静态图像、HTML 页面,甚至是 AJAX 调用。

这意味着当您登录https://examplebank.com时,会为https://examplebank.com存储一个 cookie 。如果该银行是单页 React 应用程序,他们可能已在https://examplebank.com/api创建 REST API ,以便 SPA 通过 AJAX 进行通信。

跨域漏洞

假设你 登录https://examplebank.com后浏览到一个恶意网站https://evilunicorns.com。如果没有同源策略,那么黑客网站可以对https://examplebank.com/api进行经过身份验证的恶意 AJAX 调用,即使黑客网站无法直接访问银行的 cookie。POST /withdraw

这是因为浏览器会自动将与https://examplebank.com绑定的任何 cookie 附加到该域的任何 HTTP 调用,包括从https://evilunicorns.comhttps://examplebank.com 的AJAX 调用。通过将 HTTP 调用限制为仅来自同一来源(即浏览器选项卡的域)的调用,同源策略可以关闭一些黑客后门,例如跨站点请求伪造(CSRF)(尽管不是全部。CSRF 令牌等机制仍然是必要的)。

Origin 是如何定义的?

来源包括协议、域端口的组合。这意味着https:// api .mydomain.comhttps://mydomain.com实际上是不同的来源,因此受到同源策略的影响。类似地,http://localhost: 9000http://localhost: 8080也是不同的来源。考虑来源时会忽略路径或查询参数。

来源是指发起请求的内容,通常是打开的浏览器选项卡,但也可能是 iFrame 窗口的来源。

为什么要创建 CORS?

网站发出跨域 HTTP 请求是有正当理由的。也许https://mydomain.com上的单页应用需要对https://api.mydomain.com进行 AJAX 调用;或者也许https://mydomain.com包含一些第三方字体或分析提供商,如 Google Analytics 或 MixPanel。 跨域资源共享(CORS) 支持这些跨域请求。

CORS 如何工作?

CORS 请求有两种类型:简单请求预检请求。关于请求是否预检的规则将在后面讨论。

一、简单请求

简单请求是在发起之前不需要预检请求(初步检查)的 CORS 请求。

  1. 打开浏览器选项卡以https://www.mydomain.com 发起 AJAX 请求GET https://api.mydomain.com/widgets
  2. 除了添加类似的标头之外Host,浏览器还会自动添加Origin跨源请求的请求标头:
GET /widgets/ HTTP/1.1
Host: api.mydomain.com
Origin: https://www.mydomain.com
[Rest of request...]
  1. 服务器检查Origin请求标头。如果 Origin 值被允许,则将 设置Access-Control-Allow-Origin为请求标头中的值Origin
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://www.mydomain.com
Content-Type: application/json
[Rest of response...]
  1. 当浏览器收到响应时,浏览器会检查标头以查看其是否与选项卡的来源匹配。如果不匹配,则阻止响应。如果与单一来源完全匹配或包含通配符*运算符,Access-Control-Allow-Origin则检查通过(如本例所示) 。Access-Control-Allow-Origin

响应的服务器Access-Control-Allow-Origin: * 允许所有来源,这可能带来很大的安全风险。

仅当您的应用程序绝对需要它(例如创建开放/公共 API)时才使用 *。

意图

可以看到,服务器可以根据请求的来源来决定是否允许该请求。浏览器保证Origin请求标头的设置可靠且准确。

II. 预先检查的请求

预检请求是另一种类型的 CORS 请求。预检请求是一种 CORS 请求,其中浏览器需要在发送预检请求之前发送预检请求(即初步探测),以询问服务器是否可以继续执行原始 CORS 请求。此预检请求本身是OPTIONS对同一 URL 的请求。

由于原始 CORS 请求之前有一个预检请求,因此我们将原始 CORS 请求称为已预检

如果出现以下情况,则任何 CORS 请求都必须进行预检:

  • 它使用除 GET、HEAD 或 POST 之外的方法。此外,如果使用 POST 发送 Content-Type 不是 application/x-www-form-urlencoded、multipart/form-data 或 text/plain 的请求数据(例如,如果 POST 请求使用 application/xml 或 text/xml 向服务器发送 XML 负载),则该请求已进行预检。
  • 它在请求中设置自定义标头(例如,请求使用诸如 X-PINGOTHER 之类的标头)

来源: Mozilla

需要预检请求的典型情况:
  1. 一个网站通过 AJAX 调用 POST JSON 数据到 REST API,这意味着Content-Type标头是application/json
  2. 网站对 API 进行 AJAX 调用,使用令牌在请求标头中对 API 进行身份验证,例如Authorization

这意味着,支持单页应用程序的 REST API 通常可以对大多数 AJAX 请求进行预检。

示例流程
  1. 打开浏览器选项卡,https://www.mydomain.com 启动一个POST https://api.mydomain.com/widgets带有 JSON 有效负载的经过身份验证的 AJAX 请求。浏览器OPTIONS首先发送请求(又称为预检请求),其中包含主请求的建议请求方法和请求标头:
OPTIONS /widgets/ HTTP/1.1
Host: api.mydomain.com
Origin: https://www.mydomain.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization, Content-Type
[Rest of request...]
  1. 服务器响应并指定允许的 HTTP 方法和标头。如果原始 CORS 请求旨在发送列表中不存在的标头或 HTTP 方法,则浏览器将失败,而不会尝试 CORS 请求。
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://www.mydomain.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type
Content-Type: application/json
[Rest of response...]
  1. 由于标头和方法通过了检查,浏览器将发送原始 CORS 请求。请注意,Origin此请求中也包含标头。
POST /widgets/ HTTP/1.1
Host: api.mydomain.com
Authorization: 1234567
Content-Type: application/json
Origin: https://www.mydomain.com
[Rest of request...]
  1. 响应在Access-Control-Allow-Origin标头中具有正确的来源,因此检查通过并将控制权交还给浏览器选项卡。
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://www.mydomain.com
Content-Type: application/json
[Rest of response...]

高级配置


您已经在前面的示例中看到了一些用于 CORS 的标头,例如Access-Control-Allow-OriginAccess-Control-Allow-Methods,但还有更多标头可用于更精细的控制。以下是控制 CORS 的标头的完整列表。

请求标头

标头名称示例值描述用于预检请求用于 CORS 请求
起源https://www.mydomain.com打开的浏览器选项卡的协议、域端口组合是的是的
访问控制请求方法邮政对于预检请求,指定原始 CORS 请求将使用的方法是的
访问控制请求标头授权,X-PING对于预检请求,逗号分隔的列表指定原始 CORS 请求将发送的标头是的

响应标头

标头名称示例值描述用于预检请求用于 CORS 请求
访问控制允许来源https://www.mydomain.com服务器指定的允许此请求的来源。如果与Origin标头不匹配且不是 *,浏览器将拒绝该请求。如果指定了域,则协议组件是必需的,并且只能是单个域是的是的
访问控制允许凭证真的CORS 请求通常不包含 cookie,以防止 CSRF 攻击。设置为 时true,请求可以使用/将包含 Cookie 等凭据。应省略标头以暗示falseCORS 请求不会返回到打开的选项卡。不能与通配符一起使用是的是的
访问控制公开标头日期,X 设备 ID除了默认标头之外,还要向浏览器选项卡公开其他响应标头的白名单是的
访问控制最大期限600Access-Control-Allow-Headers缓存预检请求结果(即和标头中的数据)的秒数Access-Control-Allow-Methods。Firefox 的最大值为 24 小时,Chromium 的最大值为 10 分钟。更高的值不会产生任何影响。是的
访问控制允许方法获取、发布、放置、删除可以是 * 以允许所有方法。逗号分隔的可用于 CORS 请求的允许方法白名单。是的
访问控制允许标头授权,X-PING可以是 * 以允许任何标头。以逗号分隔的允许标头白名单,可用于 CORS 请求。是的

当前浏览器尚未完全实现Access-Control-Allowed-Headers、Access-Control-Allow-Methods 和 Access-Control-Expose-Headers上的通配符 (*) 。
最好列出标头或方法

常见陷阱

1. 对 Access-Control-Allow-Origin 使用 * 运算符。

CORS 是在尝试保持安全性的同时放宽同源策略的一种方式。使用 * 会禁用 CORS 的大多数安全规则。有些情况下可以使用通配符,例如集成到许多第三方网站的开放 API。

您可以通过将 API 放在不同的域上来提高安全性。

例如,您的开放 API https://api.mydomain.com可以响应Access-Control-Allow-Origin: * ,但您主网站的 API https://www.mydomain.com/api仍然响应Access-Control-Allow-Origin: https://www.mydomain.com

2. 为 Access-Control-Allow-Origin 返回多个域。

不幸的是,规范不允许Access-Control-Allow-Origin: https://mydomain.com, https://www.mydomain.com。服务器只能使用一个域或 * 进行响应,但您可以利用Origin请求标头。

3. 使用通配符选择,如*.mydomain.com

这不是 CORS 规范的一部分,通配符只能用于表示允许所有域。

4. 不包括协议或非标准端口。

Access-Control-Allow-Origin: mydomain.com由于未包含协议,因此无效。

类似地,Access-Control-Allow-Origin: http://localhost除非服务器实际上在标准 HTTP 端口上运行,否则您将遇到麻烦:80

5. Vary 响应标头中未包含 Origin

大多数 CORS 框架都会自动执行此操作,您必须向客户端指定服务器响应将根据请求来源而有所不同。

6.未指定 Access-Control-Expose-Headers

如果未包含必需的标头,CORS 请求仍将通过,但未列入白名单的响应标头将在浏览器选项卡中隐藏。CORS 请求始终公开的默认响应标头是:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma
7.当 Access-Control-Allow-Credentials 设置为true时使用通配符

这是一个让很多人困惑的棘手案例。如果响应包含Access-Control-Allow-Credentials: true,则通配符运算符不能用于任何响应标头,例如 Access-Control-Allow-Origin。

文章来源:Authoritative guide to CORS (Cross-Origin Resource Sharing) for REST APIs

#你可能也喜欢这些API文章!