引入预检请求的动机是什么?
引入了预检请求,以便浏览器在发送某些请求之前可以确定它正在处理可识别 CORS 的服务器。这些请求被定义为具有潜在危险(状态改变)和新的(在 CORS 之前由于Same Origin Policy是不可能的)的请求。使用预检请求意味着服务器必须选择加入(通过正确响应预检)来接受 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(特别是用户的身份验证信息)这一事实。
这听起来像Cross-Site Request Forgery,其中网站上的表单B.com
可以A.com
与用户的 cookie 一起提交并造成损害。
这是正确的。另一种说法是,创建预检请求是为了不增加非 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 的预检?
是的。虽然可能不会持续很长时间。在 WebKit 浏览器中,最大预检缓存时间当前为 10 分钟。
叹。好吧,如果我知道我的服务器支持 CORS,因此不需要预检请求提供的保护,我有什么办法可以避免它们吗?
您唯一真正的选择是确保您的请求使用 CORS 安全的方法和标头。这可能意味着省略您将包含的自定义标题(如X-Requested-With
),更改Content-Type
,或更多。
无论你做什么,你都必须确保你有适当的 CSRF 保护,因为 CORS 不会阻止所有不安全的请求。正如最初的规范所说:“简单请求对检索具有重要意义的资源必须保护自己免受跨站点请求伪造”。