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

jan*_*oth 342 ajax html5 http cors preflight

跨源资源共享是一种允许网页将XMLHttpRequests发送到另一个域(来自维基百科)的机制,这非常重要(来自我:).

在过去的几天里,我一直在调整CORS,我想我对一切运作方式都有很好的理解.

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

经过相当多的搜索,我在www.w3.org(7.1.5)上找到了条信息:

为了保护资源免受在此规范存在之前不能源自某些用户代理的跨源请求,进行预检请求以确保资源知道此规范.

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

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

Mic*_*les 307

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

关键的见解是,预检请求不是安全问题.相反,它们是一个不改变规则的东西.

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

这里有三种情况:

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

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

  3. 编写了具有CORS意识的新服务器.根据标准安全实践,服务器必须在面对任何传入请求时保护其资源- 服务器不能信任客户端不做恶意事情.这种情况不会受益于预检机制:预检机制不会为正确保护其资源的服务器带来额外的安全性.

  • 如果是这种情况,为什么每次请求都会发送?每个服务器一个请求应该足以确定服务器是否知道CORS. (11认同)
  • 我同意预检请求本质上与安全性无关,但听起来像CORS'使用预检请求绝对是出于安全考虑.它不仅仅是一个完整性检查,以防止相对无害的错误情况.如果用户代理盲目地将请求发送到服务器,错误地假设服务器实现了CORS,则很可能是接受跨站点请求伪造.即使javascript无法读取响应,服务器也可能已采取了一些不良操作,例如删除帐户或进行银行转帐. (7认同)
  • 问题是,在预检结果缓存基本上是无用的,因为1.它仅适用于确切的请求,而不是整个域,因此所有的请求都将反正预检第一次; 2.实施后,在大多数浏览器中限制为10分钟,所以甚至不能无限期地接近. (5认同)
  • 该规范在浏览器中包含[preflight-result-cache](http://www.w3.org/TR/cors/#preflight-result-cache).因此,虽然它仍然感觉到kludgy和无效的安全性,但似乎可以配置新服务器以无限期地缓存预检. (3认同)
  • @VikasBansal现有服务器必须"选择加入"并同意通过配置他们如何回复预检选项请求来跨源共享资源.如果他们没有明确回答预检请求,浏览器将不会发出实际请求.毕竟,并非所有服务器都想要接受跨源请求. (2认同)

Kev*_*nry 182

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

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

这就是规范这一部分的含义:"为了保护资源免受在此规范存在之前不能源自某些用户代理的跨源请求的影响,需要进行预检请求以确保资源知道此规范."

能给我举个例子?

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

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

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

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

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

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

这听起来像跨站请求伪造,其中在网站上的表单B.com可以POSTA.com用户的cookies和进行破坏.

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

但看的要求,对于不要求preflights"简单"的要求,我看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缓存预检?

是.虽然可能不会持续很长时间.在WebKit浏览器中,最大预检缓存时间目前为10分钟.

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

你唯一真正的选择是,以确保您符合要求的"简单"的请求.这可能意味着遗漏您可能包含的自定义标题(例如X-Requested-With),说谎Content-Type或更多.

无论你做什么,你必须确保你有适当的CSRF保护,因为CORS规范没有解决拒绝"简单"请求,包括不安全的请求POST.正如规范所说:"简单请求具有除检索之外的重要性的资源必须保护自己免受跨站点请求伪造".

  • 这是我在CORS上读到的最好的介绍性文章.谢谢! (16认同)
  • 很棒的解释. (4认同)
  • 这是我在这个主题上看到的最佳答案.很好解释! (4认同)
  • CORS是一个棘手的材料,这篇文章揭示了一些隐藏点 (3认同)
  • @Yos:浏览器将包含这些 cookie,因为这就是浏览器的工作方式(如 [RFC 6265](https://tools.ietf.org/html/rfc6265#section-5.4) 等标准中所规定的那样)。浏览器是否对选项卡使用单独的进程是一个实现细节,它不会阻止它发送 cookie。 (2认同)

mon*_*sur 50

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

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

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

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

  • 可能有API依赖浏览器的同源策略来保护他们的资源.它们应该具有额外的安全性,但它们依赖于同源策略.如果没有预检,则不同域上的用户现在可以向API发出请求.API将假定请求有效(因为它不知道CORS)并执行请求.浏览器可以阻止响应到达用户,但此时可能已经完成了损坏.如果请求是PUT/DELETE,则资源可能已被更新或删除. (7认同)
  • 谢谢你的答案,这肯定完成了我的照片!不幸的是,我仍然没有看到预检背后的中心点.关于你的答案:'_uneexpected request_'会是什么?在非飞行前世界中,与预检世界(例如丢失的预检或恶意浏览器只是'忘记'预检)相比,"更多"意外/安全性如何? (5认同)
  • 你不能。我的意思是你可以做一个表单POST,*或*使用script/img做一个GET。我编辑了帖子以希望澄清这一点。 (2认同)

Kor*_*nel 32

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

一些服务器可能(差)受到保护,假设浏览器无法进行,例如跨源DELETE请求或带有X-Requested-With标头的跨源请求,因此这些请求是"可信的".

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

  • 这应该是公认的答案.这是最明确和最重要的.本质上,预检请求的唯一要点是将CORS之前的Web标准与CORS之后的Web标准集成在一起. (10认同)
  • 我喜欢这个答案,但我觉得这不是全部原因......“信任假设”必须仅适用于只有浏览器才能做的事情(特别是,发送仅限于其域的浏览器用户信息 -即饼干)。如果这不是假设的一部分,那么跨域浏览器请求可以做的任何事情都可以由第 3 方、非浏览器代理完成,对吗? (2认同)
  • @FabioBeltramini 是的,非浏览器可以发送他们想要的任何内容。然而,通过浏览器进行的攻击是特殊的,因为你可以让其他人的浏览器从他们自己的 IP、他们自己的 cookie 等做事。 (2认同)

小智 15

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

<!-- 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>
Run Code Online (Sandbox Code Playgroud)

在CORS之前,上面的漏洞尝试会失败,因为它违反了同源策略.以这种方式设计的API不需要XSRF保护,因为它受浏览器的本机安全模型保护.前CORS浏览器不可能生成跨源JSON POST.

现在CORS出现在现场 - 如果不需要通过飞行前选择加入CORS,突然这个网站会有一个巨大的漏洞,而不是他们自己的过错.

为了解释为什么某些请求可以跳过飞行前,这可以通过规范来解答:

已经将简单的跨源请求定义为与可能由当前部署的不符合本规范的用户代理生成的请求一致.

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


Hir*_*ako 13

我觉得其他答案并没有集中在预先战斗增强安全性的原因上.

场景:

1)在飞行前.当用户通过safe-bank.com验证时,攻击者伪造来自网站dummy-forums.com的请求
如果服务器未检查原点,并且某种方式存在缺陷,则浏览器将发出飞行前请求,OPTION方法.服务器不知道浏览器期望作为响应的CORS,因此浏览器不会继续(无任何伤害)

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

如果攻击者直接发送请求,来自某个随机主机,则最有可能是在考虑没有身份验证的请求.这是伪造的请求,但不是xsrf请求.所以服务器将检查凭据并失败.虽然白名单可以帮助减少这种攻击向量,但CORS并不会阻止具有凭据的攻击者发出请求.

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