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

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

在过去的几天里,我一直在摆弄CORS,我想我对一切工作都有很好的理解。

所以我的问题不是关于CORS /印前检查是如何工作的,而是关于作为新的请求types提出预检的原因 。 我没有看到为什么服务器A需要向服务器B发送预检(PR)以找出是否接受真实请求(RR)的任何理由 – B肯定有可能接受/拒绝RR任何以前的公关。

经过不lesssearch,我在www.w3.org(7.1.5)find了这条信息:

为了保护资源免受源于某些用户代理之前的跨源请求的影响,需要执行预检请求,以确保资源能够识别此规范。

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

有人可以解释一个情况/显示一个问题,PR + RR解决比RR更好吗?

我花了一些时间对于预检请求的目的感到困惑,但我想我现在已经明白了。

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

预检请求与安全无关,与CORS相关的应用程序无关。 相反,预检机制有利于在没有 CORS意识的情况下开发的服务器,并且它在客户端和服务器之间起到了CORS意识的完整性检查的作用。 CORS的开发人员认为,有足够的服务器依赖于他们永远不会收到的假设,例如跨域DELETE请求,他们发明了预检机制,允许双方selectjoin。 他们认为,只是简单地启用跨域呼叫的替代scheme将打破太多现有的应用程序。

这里有三种情况:

  1. 旧的服务器,不再在开发中,在CORS之前开发。 这些服务器可能会假设他们永远不会收到例如跨域DELETE请求。 这种情况是预检机制的主要受益者。 是的,这些服务可能已经被恶意或不合规的用户代理所滥用(CORS也没有做任何改变),但是在CORS的世界里,预检机制提供了额外的“完整性检查”,因此客户和服务器不会因为networking的基本规则已经发生了变化。

  2. 仍在开发中,但包含大量旧代码的服务器,对于所有旧代码进行审计以确保其在跨域环境中正常工作是不可行的。 这个场景允许服务器逐步selectjoinCORS,比如说“现在我允许这个特定的头文件”,“现在我允许这个特定的HTTP动词”,“现在我将允许cookies / auth信息发送“等等。 这个场景受益于预检机制。

  3. 新的CORS服务器。 根据标准的安全实践,服务器必须面对任何传入的请求来保护其资源 – 服务器不能相信客户端不会做恶意的事情。 这种情况不会从预检机制中受益 :预检机制不会为已正确保护其资源的服务器带来额外的安全性。

考虑CORS之前跨域请求的世界。 你可以做一个标准的表单POST,或者使用scriptimage标签来发出一个GET请求。 除了GET / POST之外,您不能创build任何其他请求types,也不能在这些请求上发出任何自定义标头。

随着CORS的出现,规范作者面临着在不破坏Web的现有语义的情况下引入新的跨域机制的挑战。 他们select通过向服务器提供一种方式来selectjoin任何新的请求types。 此select是预检请求。

因此,没有任何自定义标题的GET / POST请求不需要预检,因为这些请求在CORS之前已经可能了。 但是任何具有自定义头部或PUT / DELETE请求的请求需要预检,因为这些对于CORS规范来说是新的。 如果服务器对CORS一无所知,它将在没有任何CORS专用标题的情况下回复,而实际的请求将不会被创build。

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

CORS允许你指定更多的标题和方法types,而不是以前的跨源<img src><form action>

有些服务器可能(假如浏览器不能做)(例如,跨请求的DELETE请求或带有X-Requested-With标头的交叉源请求),这些请求是“可信”的,所以这些服务器可能已经(很差)了。

为了确保服务器确实支持CORS,而不是仅仅响应随机请求,就会执行预检。

介绍预检请求的动机是什么?

介绍了预检请求,以便浏览器在发送某些请求之前可以确定他们正在处理支持CORS的服务器。 这些请求被定义为那些既有潜在危险(状态改变)又有新的(由于同源策略而在CORS之前不可能)的请求。 使用印前检查请求意味着服务器必须selectjoin(通过对印前检查进行适当的响应)新的CORS使潜在的危险types的请求成为可能。

这个规范就是这样说的 :“为了保护资源免受来自某些用户代理的跨源请求的影响,在这个规范存在之前,就会做一个预检请求来确保资源能够识别这个规范。

你能举个例子吗?

假设一个用户在A.comlogin他们的银行网站。 当他们将浏览器引导至B.com我的网站时,我的页面会包含一些Javascript,它会向A.com/account发送一个恶意的DELETE请求。 由于用户在此浏览器会话中login到A.com ,因此请求中包含该域的用户身份validationCookie。 如果预检不存在,浏览器将继续发送此请求,因为在CORS下,服务器决定是否允许请求。

但是,如果A.com的银行网站不知道CORS呢? 它可能会愉快地执行DELETE 。 它不是为了防止这种攻击而devise的,因为在CORS存在之前,浏览器的同源策略会阻止它发送这样的请求。 服务器依赖于这个假设。

相反,如果浏览器首先发送预检请求,则服务器将无法正确响应。 这告诉浏览器这个服务器不参与CORS,因此它不应该发送潜在的危险的DELETE

为什么所有这些关于浏览器的问题,攻击者不能只从自己的计算机发送DELETE请求?

当然,但是这样的请求将不包括用户的cookies。 这是为了防止这种攻击依赖于浏览器将与请求一起发送其他域的cookie(特别是用户的authentication信息)的事实。

这听起来像跨网站请求伪造 ,其中B.com网站上的表单可以使用用户的Cookie发布到A.com并造成损害。

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

但是看看不需要预检的“简单”请求的要求,我看到POST仍然是允许的。 这可以改变状态和删除数据,就像DELETE

确实如此! CORS不保护您的网站免受CSRF攻击。 然后,再次,没有CORS你也没有受到CSRF攻击的保护。 预检请求的目的仅仅是将CSRF攻击面限制在CORS之前的世界中。

叹。 好,我勉强接受预检请求的需要。 但是,为什么我们必须为服务器上的每个资源(URL)执行此操作? 服务器要么处理CORS,要么不处理。

你确定吗? 多个服务器处理单个域的请求并不罕见。 尤其是, A.com/url1请求可能由一种服务器处理,而A.com/url2请求则由不同types的服务器处理。 一般情况下,处理单个资源的服务器可以对该域上的所有资源进行安全保证。

精细。 让我们妥协。 让我们创build一个新的CORS头,让服务器确切地说明它可以说的资源,从而避免对这些URL进行额外的预检请求。

好主意! 实际上,头部Access-Control-Policy-Path就是为此而提出的。 但是最终,它被排除在规范之外, 显然是因为一些服务器错误地实现了URI规范,使得浏览器看起来安全的请求对于破坏的服务器来说实际上并不安全。

这是一个谨慎的决定,优先考虑安全而不是性能,允许浏览器立即实施CORS规范,而不会使现有服务器处于危险之中? 或者是因为互联网浪费了带宽,并且为了适应特定时间的特定服务器中的错误而加倍延迟?

意见不一。

那么,至less浏览器会caching一个URL的预检?

是。 虽然可能不会很长。 在WebKit浏览器中,最高预检caching时间目前为10分钟 。

啊。 那么,如果我知道我的服务器是CORS意识的,因此不需要预检请求提供的保护,有什么方法可以避免它们?

你唯一的select是确保你满足“简单”请求的要求。 这可能意味着遗漏自定义标题,否则将包含(如X-Requested-With ),说谎的Content-Type ,或更多。

无论如何,由于CORS规范并不涉及拒绝“简单”请求,包括不安全的POST ,所以你要确保你有适当的CSRF保护。 正如规范所说 :“对于简单请求具有除检索以外的其他重要资源的资源必须保护自己免于跨站点请求伪造”。

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

 <!-- 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> 

Pre-CORS,上面的攻击尝试会失败,因为它违反了同源策略。 这样devise的API不需要XSRF保护,因为它受到了浏览器本地安全模型的保护。 CORS之前的浏览器不可能生成一个跨源的JSON POST。

现在CORS出现了 – 如果不需要通过预先的飞行来selectCORS,突然间,这个站点将会有一个巨大的漏洞,而不是他们自己的错误。

为了解释为什么一些请求被允许跳过预先的飞行, 这是规范回答:

一个简单的跨域请求被定义为与当前部署的不符合本规范的用户代理可能产生的请求一致。

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

我觉得其他的答案是不关注预先战斗增强安全性的原因。

场景:

1) 在飞行前 。 当用户通过safe-bank.com进行身份validation时,攻击者伪造来自站点dummy-forums.com的请求
如果服务器没有检查来源,并且有某种缺陷, 浏览器会发出一个预先的请求,OPTION方法。 服务器不知道浏览器期望的CORS作为响应,所以浏览器将不会继续(无论如何)

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

如果攻击者直接发送一个请求,那么来自某个随机主机的交叉源,很有可能是一个没有authentication的请求。 这是一个伪造的请求,但不是一个xsrf之一。 所以服务器将检查凭据并失败。 尽pipe白名单可以帮助减less这种攻击,CORS并不试图阻止拥有证书的攻击者发出请求。

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

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

资源

不是关于性能的预检请求? 通过预先发送的请求,客户端可以在发送大量数据(例如,使用PUT方法的JSON)之前快速知道是否允许操作。 或者在通过线路传输身份validation标头中的敏感数据之前。

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

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

我不明白引用前的请求和旧服务器的好处之间的相关性。 在CORS之前实现的Web服务,或没有CORS认识的Web服务将永远不会收到任何跨站请求,因为他们的响应首先不会有“Access-Control-Allow-Origin”标头。

在支持CORS的浏览器中, 读取请求(如GET)已受到同源策略的保护:试图进行经过validation的跨域请求的恶意网站(例如受害者的网上银行网站或路由器的configuration界面)不会能够读取返回的数据,因为银行或路由器没有设置Access-Control-Allow-Origin头。

然而,在请求(如POST)时,请求到达Web服务器时会造成损坏。* Web服务器可以检查Origin头以确定请求是否合法,但是此检查通常不会实现,因为Web服务器没有需要CORS或者networking服务器比CORS旧,因此假设跨域POST是完全被同源策略禁止的。

这就是为什么web服务器有机会select接受跨域写请求

*基本上是CSRF的AJAX版本。

Interesting Posts