什么是引入预检请求的动机?
引入预检请求是为了让浏览器在发送某些请求之前确定它正在处理一个CORS感知服务器。这些请求被定义为潜在危险(改变状态)且新的(由于同源策略,在CORS之前不可能)。使用预检请求意味着服务器必须选择加入(通过正确响应预检请求)到CORS使得可能的新的、潜在危险的请求类型中。
这就是原始规范this part的含义:“为了保护资源免受跨域请求的影响,这些请求在此规范出现之前无法从某些用户代理发起,因此需要进行预检请求以确保该资源已经意识到此规范。”
你能给我一个例子吗?
假设浏览器用户已登录其银行网站
A.com
。当他们导航到恶意的
B.com
时,该页面包括一些 JavaScript,试图向
A.com/account
发送一个
DELETE
请求。由于用户已登录
A.com
,如果发送该请求,它将包括标识用户的 cookie。
在 CORS 之前,浏览器的同源策略会阻止它发送此请求。但是,由于 CORS 的目的是使这种跨源通信成为可能,因此这不再适用。
浏览器可以简单地发送
DELETE
并让服务器决定如何处理它。但是,如果
A.com
不知道 CORS 协议怎么办?它可能会继续执行危险的
DELETE
。它可能已经假定由于浏览器的同源策略,它永远不会收到这样的请求,因此可能从未针对这种攻击进行过硬化。
为了保护这些不支持CORS的服务器,协议要求浏览器首先发送一个
预检请求(preflight request)。只有支持CORS的服务器才能正确响应这种新类型的请求,让浏览器知道是否安全地发送实际的
DELETE
请求。
为什么要大费周折涉及到浏览器呢?攻击者不能直接从他们自己的计算机发送DELETE
请求吗?
当然可以,但这样的请求不会包含用户的cookies。这个设计旨在防止一种攻击行为,该攻击行为依赖于浏览器将来自其他域的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不会阻止所有不安全的请求。正如原始规范所述:“除了检索之外,简单请求具有其他含义的资源必须保护自己免受跨站点请求伪造的攻击。”