跨源资源共享是一种机制,允许网页向另一个域发送xmlhttprequest(来自维基百科)。

在过去的几天里,我一直在摆弄CORS,我认为我对每件事情的工作原理都有很好的理解。

所以我的问题不是关于CORS /预飞是如何工作的,而是关于将预飞作为一种新的请求类型背后的原因。我看不出为什么服务器A需要发送一个preflight (PR)到服务器B,只是为了找出真实的请求(RR)是否会被接受——B当然有可能在没有任何预先PR的情况下接受/拒绝RR。

在搜索了相当多之后,我在www.w3.org(7.1.5)上找到了这条信息:

为了保护资源不受在本规范存在之前不能来自某些用户代理的跨源请求的影响,a 发出飞行前请求,以确保资源知道这一点 规范。

我发现这是最难理解的句子。我的解释(最好称之为“最佳猜测”)是关于保护服务器B不受服务器C的请求,因为服务器C不知道该规范。

谁能解释一个场景/展示一个PR + RR比RR单独解决更好的问题?


当前回答

我花了一段时间对飞行前请求的目的感到困惑,但我想我现在明白了。

关键的观点是,飞行前请求不是一个安全问题。相反,它们是一种不改变规则的东西。

Preflight requests have nothing to do with security, and they have no bearing on applications that are being developed now, with an awareness of CORS. Rather, the preflight mechanism benefits servers that were developed without an awareness of CORS, and it functions as a sanity check between the client and the server that they are both CORS-aware. The developers of CORS felt that there were enough servers out there that were relying on the assumption that they would never receive, e.g. a cross-domain DELETE request that they invented the preflight mechanism to allow both sides to opt-in. They felt that the alternative, which would have been to simply enable the cross-domain calls, would have broken too many existing applications.

这里有三种场景:

Old servers, no longer under development, and developed before CORS. These servers may make assumptions that they'll never receive e.g. a cross-domain DELETE request. This scenario is the primary beneficiary of the preflight mechanism. Yes these services could already be abused by a malicious or non-conforming user agent (and CORS does nothing to change this), but in a world with CORS the preflight mechanism provides an extra 'sanity check' so that clients and servers don't break because the underlying rules of the web have changed. Servers that are still under development, but which contain a lot of old code and for which it's not feasible/desirable to audit all the old code to make sure it works properly in a cross-domain world. This scenario allows servers to progressively opt-in to CORS, e.g. by saying "Now I'll allow this particular header", "Now I'll allow this particular HTTP verb", "Now I'll allow cookies/auth information to be sent", etc. This scenario benefits from the preflight mechanism. New servers that are written with an awareness of CORS. According to standard security practices, the server has to protect its resources in the face of any incoming request -- servers can't trust clients to not do malicious things. This scenario doesn't benefit from the preflight mechanism: the preflight mechanism brings no additional security to a server that has properly protected its resources.

其他回答

CORS允许您指定比以前cross-origin <img src>或<form action>更多的头文件和方法类型。

一些服务器可能已经(很差地)受到了浏览器无法做出的假设的保护,例如跨源DELETE请求或带有X-Requested-With头的跨源请求,因此这样的请求是“可信的”。

为了确保服务器真正支持CORS,而不是恰好响应随机请求,执行了preflight。

下面是另一种看待它的方式,使用代码:

<!-- hypothetical exploit on evil.com -->
<!-- Targeting banking-website.example.com, which authenticates with a cookie -->
<script>
jQuery.ajax({
  method: "POST",
  url: "https://banking-website.example.com",
  data: JSON.stringify({
    sendMoneyTo: "Dr Evil",
    amount: 1000000
  }),
  contentType: "application/json",
  dataType: "json"
});
</script>

在cors之前,上述利用尝试将会失败,因为它违反了同源策略。以这种方式设计的API不需要XSRF保护,因为它受到浏览器本地安全模型的保护。cors之前的浏览器不可能生成跨源JSON POST。

现在CORS出现了——如果不需要通过飞行前选择加入CORS,突然之间这个网站就会有一个巨大的漏洞,这不是他们自己的过错。

为了解释为什么一些请求被允许跳过预飞行,这是在规范中回答的:

简单的跨源请求被定义为与这些请求一致 哪些可能是由当前部署的用户代理生成的 遵守这个规范。

为了解决这个问题,GET没有预先运行,因为它是7.1.5中定义的“简单方法”。(标题也必须“简单”,以避免预飞行)。 这样做的理由是,“简单的”跨源GET请求已经可以通过例如<script src="">来执行(这是JSONP的工作方式)。由于任何具有src属性的元素都可以触发跨源GET,没有预先飞行,因此在“简单”xhr上要求预先战斗将没有安全好处。

此外,对于HTTP请求方法可能导致的副作用 用户数据(特别是对于GET以外的HTTP方法或POST 使用特定的MIME类型),规范要求 浏览器“预先处理”请求

考虑一下CORS之前的跨域请求世界。您可以执行标准表单POST,或者使用脚本或图像标记来发出GET请求。除了GET/POST之外,您不能创建任何其他请求类型,并且不能对这些请求发出任何自定义头。

随着CORS的出现,规范作者面临着在不破坏现有web语义的情况下引入新的跨域机制的挑战。他们选择通过为服务器提供一种选择任何新请求类型的方法来做到这一点。这个选择是飞行前的请求。

因此,没有任何自定义报头的GET/POST请求不需要preflight,因为这些请求在CORS之前就已经可以实现了。但是任何带有自定义报头的请求,或PUT/DELETE请求,都需要预先处理,因为这些是CORS规范的新内容。如果服务器对CORS一无所知,它将在没有任何CORS特定报头的情况下进行回复,并且不会发出实际的请求。

如果没有preflight请求,服务器可能会开始看到来自浏览器的意外请求。如果服务器没有为这些类型的请求做好准备,这可能会导致安全问题。CORS preflight允许以安全的方式将跨域请求引入web。

在支持CORS的浏览器中,读取请求(如GET)已经受到同源策略的保护:试图发出经过身份验证的跨域请求(例如向受害者的网上银行网站或路由器的配置接口)的恶意网站将无法读取返回的数据,因为银行或路由器没有设置Access-Control-Allow-Origin报头。

然而,对于写请求(如POST),当请求到达web服务器时就会造成损害。* web服务器可以检查Origin报头来确定请求是否合法,但这种检查通常不会实现,因为web服务器不需要CORS,或者web服务器比CORS更老,因此假设跨域post完全被同源策略禁止。

这就是为什么web服务器有机会选择接收跨域写请求。

本质上是CSRF的AJAX版本。