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

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

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

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

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

保护资源,防止在本规范存在之前不能来自某些用户代理的跨源请求 发出飞行前请求,以确保资源知道这一点 规范。< / em > < / p >

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

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

97813 次浏览

CORS允许你指定更多的头文件和方法类型,而不是以前使用跨源<img src><form action>

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

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

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

Source .

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

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

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

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

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

关键是飞行前请求不是一个安全的东西。相反,它们是not-changing-the-rules的东西。

飞行前请求与安全无关,它们与现在正在开发的应用程序没有任何关系,因为了解了CORS。相反,preflight机制有利于开发没有的服务器意识到CORS,并且它在客户端和服务器之间作为一个健全的检查,它们都是CORS意识。CORS的开发人员认为有足够多的服务器依赖于他们永远不会收到的假设,例如跨域DELETE请求,他们发明了preflight机制,允许双方选择加入。他们认为另一种选择是简单地启用跨域调用,这会破坏太多现有的应用程序。

这里有三种场景:

  1. 旧服务器,不再处于开发阶段,是在CORS之前开发的。这些服务器可能会假设它们永远不会收到例如跨域DELETE请求。这种情况是飞行前机制的主要受益者。是的,这些服务可能已经被恶意或不符合规范的用户代理滥用(CORS没有做任何改变),但在CORS的世界中,preflight机制提供了额外的“健全检查”,这样客户端和服务器就不会因为网络的底层规则已经改变而崩溃。

  2. 仍在开发中,但包含大量旧代码的服务器,对于这些服务器,审计所有旧代码以确保其在跨域世界中正常工作是不可行的/不可取的。此场景允许服务器逐步选择加入CORS,例如,通过说“现在我将允许这个特定的报头”,“现在我将允许这个特定的HTTP动词”,“现在我将允许发送cookie /auth信息”等

  3. 使用CORS意识编写的新服务器。根据标准的安全实践,服务器必须在面对任何传入请求时保护其资源——服务器不能相信客户端不会做恶意的事情。此场景不受益于preflight机制: preflight机制不会为已经正确保护其资源的服务器带来额外的安全性。

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

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

“我看到你们允许

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

场景:

< p > 1) 在飞行前。攻击者伪造来自站点dummy-forums.com的请求,而用户的身份验证是safe-bank.com
如果服务器没有检查源,并且以某种方式存在缺陷,浏览器将发出一个pre-flight请求,OPTION方法。服务器不知道浏览器期望作为响应的CORS,因此浏览器将不会继续(没有任何伤害)

2) 没有飞行前。攻击者在上面相同的场景下伪造请求,浏览器将立即发出POST或PUT请求,服务器接受它并可能处理它,这可能会造成一些危害。

< / p > 如果攻击者直接从某个随机主机发送请求,跨越原点,那么最有可能的是一个具有没有身份验证的请求。这是伪造的请求,但不是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并让服务器决定如何处理它。但是如果A.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版本。