CORS - 引入预检请求的动机是什么?

时间:2013-03-13 09:16:10

标签: ajax html5 http cors preflight

跨域资源共享是一种允许网页将XMLHttpRequests发送到另一个域(来自wikipedia)的机制。

过去几天我一直在玩CORS,我觉得我对一切运作方式都有很好的理解。

所以我的问题不是关于CORS /预检如何工作,而是关于将预检作为新请求类型的原因。我没有看到任何理由为什么服务器A需要向服务器B发送预检(PR)以确定是否接受真实请求(RR) - B当然可以接受/拒绝RR而不用任何先前的公关。

经过大量搜索,我在www.w3.org(7.1.5)上找到了this piece的信息:

  

在此规范存在之前,保护资源免受源自某些用户代理的跨源请求的影响   执行预检请求以确保资源知道这一点   说明书

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

有人可以解释一个场景/显示PR + RR解决的问题比单独的RR更好吗?

9 个答案:

答案 0 :(得分:280)

我花了一些时间对飞行前请求的目的感到困惑,但我想我现在已经得到了它。

关键的见解是,预检请求不是安全的事情。相反,它们是不改变规则的东西。

预检请求与安全性无关,它们与现在正在开发的应用程序无关,并且了解CORS。相反,预检机制有利于在没有意识到CORS的情况下开发的服务器,并且它可以作为客户端和服务器之间的完整性检查,它们都是CORS感知的。 CORS的开发人员觉得有足够的服务器依赖于他们永远不会收到的假设,例如跨域DELETE请求,他们发明了预检机制,允许双方选择加入。他们认为,只需启用跨域调用的替代方案就会破坏太多现有应用程序。

这里有三种情况:

  1. 旧服务器,不再处于开发阶段,并且在CORS之前开发。这些服务器可以做出他们永远不会收到的假设,例如跨域DELETE请求。 这种情况是预检机制的主要受益者。是的,这些服务可能已经被恶意或不符合要求的用户代理滥用(并且CORS没有做任何改变),但在CORS的世界中预检机制提供额外的“健全性检查”,以便客户端和服务器不会因为Web的基本规则发生变化而中断。

  2. 仍在开发中但包含大量旧代码的服务器,并且审计所有旧代码以确保它在跨域社区中正常工作是不可行/不可取的。这种情况允许服务器逐步选择加入CORS,例如通过说“现在我将允许这个特定的标题”,“现在我将允许这个特定的HTTP动词”,“现在我将允许发送cookie / auth信息”,等等。这个场景得益于预检机制。

  3. 使用CORS认识编写的新服务器。根据标准安全实践,服务器必须在面对任何传入请求时保护其资源 - 服务器不能信任客户端不做恶意事情。 此方案无法从预检机制中受益:预检机制不会为正确保护其资源的服务器带来额外的安全性。

答案 1 :(得分:135)

引入预检请求的动机是什么?

引入了预检请求,以便浏览器在发送某些请求之前可以确定它正在处理支持CORS的服务器。这些请求被定义为具有潜在危险(状态变化)和新的(由于Same Origin Policy而在CORS之前不可能)。使用预检请求意味着服务器必须选择加入(通过正确响应预检)以使CORS能够实现的新的,潜在危险的请求类型。

这是this part of the specification的含义:"为了保护资源免受在此规范存在之前不能源自某些用户代理的跨源请求,进行了预检请求以确保资源知道这个规范。"

你能举个例子吗?

让我们想象一下,浏览器用户登录A.com的银行网站。当他们导航到恶意B.com时,该页面会包含一些尝试向DELETE发送A.com/account请求的Javascript。由于用户已登录A.com,该请求(如果已发送)将包含用于标识用户的Cookie。

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

浏览器可以简单地发送DELETE并让服务器决定如何处理它。但是如果A.com没有意识到CORS协议呢?它可能继续执行危险的DELETE。它可能假设 - 由于浏览器的同源策略 - 它永远不会收到这样的请求,因此可能从未加强过这种攻击。

为保护此类非CORS感知服务器,协议要求浏览器首先发送预检请求。这种新的请求只有CORS感知服务器才能正确响应,允许浏览器知道发送实际DELETE是否安全。

为什么所有关于浏览器的大惊小怪,攻击者只能从他们自己的计算机发送DELETE请求?

当然,但这样的请求不会包含用户的cookie。旨在防止此攻击的攻击依赖于浏览器将为请求发送其他域的cookie(特别是用户的身份验证信息)这一事实。

听起来像Cross-Site Request Forgery,网站B.com上的表单可以使用用户的Cookie POSTA.com损坏。

那是对的。另一种方法是创建预检请求,以便不会增加非CORS感知服务器的CSRF攻击面。

但是看{4}}"简单"不需要预检的请求,我发现仍然允许POST。这可以改变状态并删除数据,就像DELETE

一样

那是真的! CORS不保护您的站点免受CSRF攻击。然后,如果没有CORS,您也无法免受CSRF攻击。预检请求的目的只是将您的CSRF暴露于CORS前世界已经存在的范围。

<强> 叹息。好的,我勉强接受了对飞行前请求的需求。但为什么我们必须为服务器上的每个资源(URL)执行此操作?服务器要么处理CORS,要么没有处理。

你确定吗?多个服务器处理单个域的请求并不罕见。例如,可能是某种服务器处理对A.com/url1的请求,而A.com/url2的请求由不同类型的服务器处理。通常情况下,处理单个资源的服务器可以对该域上的所有资源提供安全保证。

<强> 精细。让我们妥协吧。让我们创建一个新的CORS标头,允许服务器准确说明它可以代表哪些资源,这样就可以避免对这些网址的额外预检请求。

好主意!事实上,标题Access-Control-Policy-Path就是出于此目的而提出的。但最终,它被排除在规范requirements之外,因为某些服务器错误地实现了URI规范,这样对浏览器似乎安全的路径的请求实际上在损坏的服务器上是不安全的。

这是一个谨慎的决定,优先考虑安全性而非性能,允许浏览器立即实施CORS规范而不会使现有服务器面临风险吗?或者,为了在特定时间容纳特定服务器中的错误而使互联网浪费带宽和延迟加倍是否短视?

意见不同。

嗯,至少浏览器会为单个网址缓存预检?

是。虽然可能不会持续很长时间。在WebKit浏览器中,最大预检缓存时间为apparently

<强> 叹息。好吧,如果我知道我的服务器是CORS感知的,因此不需要预检请求提供的保护,我有什么办法可以避免它们吗?

您唯一真正的选择是确保您符合currently 10 minutes的简单&#34;要求。这可能意味着遗漏您可能包含的自定义标头(例如X-Requested-With),位于Content-Type或更多位置。

无论你做什么,你必须确保你有适当的CSRF保护,因为CORS规范没有解决拒绝&#34;简单&#34;请求,包括不安全的POSTrequirements:&#34;简单请求具有除检索之外的重要性的资源必须保护自己免受跨站请求伪造的侵害#34;。

答案 2 :(得分:49)

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

随着CORS的出现,规范作者面临着在不破坏网络现有语义的情况下引入新的跨域机制的挑战。他们选择通过为服务器提供选择加入任何新请求类型的方式来实现此目的。此选择加入是预检请求。

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

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

答案 3 :(得分:30)

CORS允许您指定比以前使用跨源<img src><form action>更多的标头和方法类型。

某些服务器可能已被(差)保护,假设浏览器无法做到,例如具有DELETE标头的跨源X-Requested-With请求或跨源请求,因此此类请求是“受信任的”。

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

答案 4 :(得分:14)

以下是使用代码查看它的另一种方式:

<!-- 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,突然这个网站会有一个巨大的漏洞,而不是他们自己的过错。

解释为什么某些请求可以跳过飞行前this is answered by the spec:

  

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

为了解开这个问题,GET不是预先确定的,因为它是7.1.5定义的“简单方法”。 (标题也必须“简单”以避免飞行前)。 对此的理由是“简单的”跨源GET请求可能已经由例如<script src="">(这就是JSONP的工作原理)。由于具有src属性的任何元素都可以触发跨源GET,没有预先飞行,因此要求对“简单”XHR进行预打击没有任何安全优势。

答案 5 :(得分:12)

I feel that the other answers aren't focusing on the reason pre-fight enhances security.

Scenarios:

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.

If the attacker sends a request directly, cross origin, from some random host it's most likely one is thinking about a request with no authentication. That's a forged request, but not a xsrf one. so the server has will check credentials and fail. CORS doesn't attempt to prevent an attacker who has the credentials to issue requests, although a whitelist could help reduce this vector of attack.

The pre-flight mechanism adds safety and consistency between clients and servers. I don't know if this is worth the extra handshake for every request since caching is hardy use-able there, but that's how it works.

答案 6 :(得分:3)

  

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

Source

答案 7 :(得分:2)

飞行前请求对于可以更改服务器状态的请求是必需的。有2种类型的请求-

1)不能更改服务器状态的呼叫(如GET)-用户可能会收到请求的响应(如果服务器未检查来源),但请求域是没有添加到响应标头Access-Control-Allow-Origin中,浏览器不会向用户显示数据,即,请求是从浏览器发送的,但用户无法查看/使用响应。

2)可以更改服务器状态的调用(例如POST,DELETE)-从1开始,我们看到浏览器不会阻止请求,而是阻止响应,状态更改的调用未经事先检查,不得进行。即使对浏览器的响应可能失败,此类调用也可能会更改不检查调用源的信任服务器(称为跨站点请求伪造)。因此,我们具有预检请求的概念,该请求在将任何状态更改的调用发送到服务器之前先进行OPTIONS调用。

答案 8 :(得分:1)

Aren关于效果的预检请求?利用预检请求,客户端可以在发送大量数据之前快速知道是否允许操作,例如,使用PUT方法在JSON中。或者通过线路在认证标题中传输敏感数据之前。

除了自定义标题之外,PUT,DELETE和其他方法的事实在默认情况下是不允许的(他们需要明确的权限,使用&#34;访问控制 - 请求 - 方法&#34;和&#34 ; Access-Control-Request-Headers&#34;),听起来就像是一次双重检查,因为这些操作可能会对用户数据产生更多影响,而不是GET请求。 所以,听起来像是:

&#34;我看到您允许来自http://foo.example的跨网站请求,但您确定您是否允许DELETE请求?您是否考虑过这些请求可能对用户数据造成的影响?&#34;

我不理解预检请求与旧服务器优势之间的引用关联。在CORS之前实现或没有CORS感知的Web服务将永远不会收到任何跨站点请求,因为首先他们的响应不会获得&#34; Access-Control-Allow-Origin&#34;报头中。