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

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

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

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

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

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

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


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

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

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


此外,对于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。


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

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

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.


预先飞行的请求不是关于性能的吗?通过预飞行请求,客户端可以在发送大量数据之前快速知道该操作是否允许,例如,在JSON中使用PUT方法。或者在通过网络传输身份验证头中的敏感数据之前。

事实上,除了自定义头之外,默认情况下不允许使用PUT、DELETE和其他方法(它们需要“Access-Control-Request-Methods”和“Access-Control-Request-Headers”的显式权限),这听起来就像一个双重检查,因为这些操作可能会对用户数据产生更多影响,而不是GET请求。 听起来是这样的:

“我看到你允许来自http://foo.example的跨站请求,但你确定你会允许删除请求吗?你考虑过这些请求可能对用户数据造成的影响吗?”

我不理解所引用的预飞行请求和旧服务器的好处之间的相关性。在CORS之前实现的Web服务,或者没有CORS意识的Web服务,将永远不会接收任何跨站点请求,因为首先它们的响应不会有“Access-Control-Allow-Origin”报头。


我觉得其他的答案并没有把重点放在战前提高安全性的原因上。

场景:

1) With pre-flight. An attacker forges a request from site dummy-forums.com while the user is authenticated to safe-bank.com If the Server does not check for the origin, and somehow has a flaw, the browser will issue a pre-flight request, OPTION method. The server knows none of that CORS that the browser is expecting as a response so the browser will not proceed (no harm whatsoever) 2) Without pre-flight. An attacker forges the request under the same scenario as above, the browser will issue the POST or PUT request right away, the server accepts it and might process it, this will potentially cause some harm.

如果攻击者直接从某个随机主机发送请求,那么很有可能是没有经过身份验证的请求。这是伪造的请求,但不是xsrf请求。服务器会检查凭证并失败。 CORS并不试图阻止拥有发出请求的凭据的攻击者,尽管白名单可以帮助减少这种攻击向量。

预飞行机制增加了客户端和服务器之间的安全性和一致性。 我不知道这是否值得为每个请求额外握手,因为缓存在那里是可以使用的,但这就是它的工作原理。


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

<!-- 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上要求预先战斗将没有安全好处。


引入飞行前请求的动机是什么?

引入了Preflight请求,以便浏览器在发送特定请求之前可以确定它正在处理一个支持cors的服务器。这些请求被定义为既具有潜在危险(状态改变)又具有新请求(由于同源政策,在CORS之前是不可能的)。使用飞行前请求意味着服务器必须选择(通过正确地响应飞行前请求)接受CORS使之成为可能的新的、具有潜在危险的请求类型。

这就是原始规范的这一部分的含义:“为了保护资源不受在此规范存在之前不能来自某些用户代理的跨起源请求的影响,发出预飞行请求以确保资源知道此规范。”

你能给我举个例子吗?

让我们假设一个浏览器用户登录到他们的银行网站A.com。当他们导航到恶意的B.com时,该页面包含一些试图向A.com/account发送DELETE请求的Javascript。由于用户登录到A.com,如果发送该请求,将包括识别用户的cookie。

在CORS之前,浏览器的同源策略会阻止它发送此请求。但由于CORS的目的是使这种跨起源通信成为可能,这已经不合适了。

浏览器可以简单地发送DELETE并让服务器决定如何处理它。但是如果。com不知道CORS协议怎么办?它可能会继续执行危险的DELETE。由于浏览器的同源策略(Same Origin policy),它可能认为它永远不会收到这样的请求,因此它可能从来没有对这样的攻击进行过加固。

为了保护这些不支持cors的服务器,该协议要求浏览器首先发送一个飞行前请求。这种新的请求只有支持cors的服务器才能正确响应,从而允许浏览器知道发送实际的DELETE是否安全。

为什么浏览器这么麻烦,攻击者就不能从他们自己的电脑发送一个DELETE请求吗?

当然,但是这样的请求不包括用户的cookie。这种设计用来防止的攻击依赖于这样一个事实:浏览器将在请求的同时为其他域发送cookie(特别是用户的身份验证信息)。

这听起来像是跨站请求伪造,即站点B.com上的表单可以用用户的cookie提交给A.com,并造成破坏。

这是正确的。另一种说法是,创建预飞行请求是为了不增加非cors感知服务器的CSRF攻击面。

但是POST被列为一种不需要预先操作的方法。它可以改变状态和删除数据,就像delete !

这是真的!CORS不能保护您的站点免受CSRF攻击。然而,如果没有CORS,您也无法免受CSRF攻击。飞行前请求的目的只是限制你的CSRF暴露在cors之前已经存在的世界。

叹息。好的,我勉强接受飞行前的要求。但是为什么我们必须对服务器上的每个资源(URL)都这样做呢?服务器要么处理CORS,要么不处理。

你确定吗?多个服务器处理单个域的请求并不罕见。例如,对A.com/url1的请求可能由一种服务器处理,而对A.com/url2的请求由另一种服务器处理。处理单个资源的服务器通常不能对该域中的所有资源提供安全保证。

好吧。让我们妥协。让我们创建一个新的CORS头,它允许服务器确切地声明它可以代表哪些资源,这样就可以避免对这些url的额外预飞行请求。

好主意!事实上,头文件Access-Control-Policy-Path正是为此目的而提出的。但最终,它被排除在规范之外,这显然是因为一些服务器错误地实现了URI规范,使得对浏览器来说似乎安全的路径的请求在损坏的服务器上实际上并不安全。

这是一个谨慎的决定,优先考虑安全性而不是性能,允许浏览器立即实现CORS规范,而不会使现有服务器处于危险之中?或者,仅仅为了在特定时间适应特定服务器上的漏洞,就注定要浪费互联网的带宽和加倍的延迟,这是短视的做法吗?

意见不同。

那么,至少浏览器会为单个URL缓存preflight ?

是的。虽然可能不会持续太久。在WebKit浏览器中,最大的预缓存时间目前是10分钟。

叹息。好吧,如果我知道我的服务器是cors感知的,因此不需要飞行前请求提供的保护,有什么方法可以避免它们吗?

您唯一的选择是确保您的请求使用cors安全的方法和头。这可能意味着省略您本来应该包含的自定义头文件(如X-Requested-With),更改Content-Type等等。

无论您做什么,都必须确保有适当的CSRF保护,因为CORS不会阻止所有不安全的请求。正如最初的规范所说:“对于那些简单的请求有意义而不是检索的资源,必须保护自己不受跨站点请求伪造的侵害”。


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

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

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

本质上是CSRF的AJAX版本。