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

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

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

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

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

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

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


当前回答

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

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

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

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

其他回答

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

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

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

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

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

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

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

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

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

本质上是CSRF的AJAX版本。

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

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

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

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

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