为啥经过身份验证的 CORS 请求的预检 OPTIONS 请求在 Chrome 中有效,但在 Firefox 中无效?

Posted

技术标签:

【中文标题】为啥经过身份验证的 CORS 请求的预检 OPTIONS 请求在 Chrome 中有效,但在 Firefox 中无效?【英文标题】:Why does the preflight OPTIONS request of an authenticated CORS request work in Chrome but not Firefox?为什么经过身份验证的 CORS 请求的预检 OPTIONS 请求在 Chrome 中有效,但在 Firefox 中无效? 【发布时间】:2013-03-21 23:19:26 【问题描述】:

我正在编写一个 javascript 客户端以包含在 3rd 方网站上(想想 Facebook 的 Like 按钮)。它需要从需要基本 HTTP 身份验证的 API 中检索信息。简化的设置如下所示:

第 3 方网站在其页面上包含此 sn-p:

<script 
async="true"
id="web-dev-widget"
data-public-key="pUbl1c_ap1k3y"
src="http://web.dev/widget.js">
</script>

widget.js 调用 API:

var el = document.getElementById('web-dev-widget'),
    user = 'token',
    pass = el.getAttribute('data-public-key'),
    url = 'https://api.dev/',
    httpRequest = new XMLHttpRequest(),
    handler = function() 
      if (httpRequest.readyState === 4) 
        if (httpRequest.status === 200) 
          console.log(httpRequest.responseText);
         else 
          console.log('There was a problem with the request.', httpRequest);
        
      
    ;

httpRequest.open('GET', url, true, user, pass);
httpRequest.onreadystatechange = handler;
httpRequest.withCredentials = true;
httpRequest.send();

API 已配置为使用适当的标头进行响应:

Header set Access-Control-Allow-Credentials: true
Header set Access-Control-Allow-Methods: "GET, OPTIONS"
Header set Access-Control-Allow-Headers: "origin, authorization, accept"
SetEnvIf Origin "http(s)?://(.+?\.[a-z]3)$" AccessControlAllowOrigin=$0
Header set Access-Control-Allow-Origin %AccessControlAllowOrigine env=AccessControlAllowOrigin

请注意,Access-Control-Allow-Origin 设置为 Origin,而不是使用通配符,因为我正在发送凭据请求 (withCredentials)。

现在一切就绪,可以发出异步跨域身份验证请求,并且在 OS X 10.8.2 上的 Chrome 25 中运行良好。在开发工具中,我可以看到OPTIONS 请求在GET 请求之前的网络请求,并且响应按预期返回。

在 Firefox 19 中测试时,Firebug 中没有出现对 API 的网络请求,并且在控制台中记录了此错误:NS_ERROR_DOM_BAD_URI: Access to restricted URI denied

经过大量挖掘,我发现Gecko doesn't allow the username and password to be directly in a cross-site URI根据cmets。我假设这是使用可选的用户和密码参数到open(),所以我尝试了另一种进行身份验证的请求的方法,即对凭据进行 Base64 编码并发送授权标头:

// Base64 from http://www.webtoolkit.info/javascript-base64.html
auth = "Basic " + Base64.encode(user + ":" + pass);

...
// after open() and before send()
httpRequest.setRequestHeader('Authorization', auth);

这导致401 UnauthorizedOPTIONS 请求的响应导致Google 搜索,例如“为什么这在Chrome 而不是Firefox 中有效!?”那时我知道我遇到了麻烦。

为什么 它在 Chrome 而不是 Firefox 中工作?如何让OPTIONS 请求一致地发送和响应?

【问题讨论】:

我很想就如何使问题变得更好提供意见 【参考方案1】:

为什么 它在 Chrome 而不是 Firefox 中工作?

W3 spec for CORS preflight requests 明确指出应排除用户凭据。 Chrome 和 WebKit 中存在一个错误,其中 OPTIONS 请求返回状态 401 仍会发送后续请求。

Firefox 有一个相关的错误提交,该错误以指向 W3 public webapps mailing list 的链接结尾,要求更改 CORS 规范以允许在 OPTIONS 请求上发送身份验证标头,这对 IIS 用户有利。基本上,他们正在等待那些服务器被淘汰。

我怎样才能让OPTIONS 请求始终如一地发送和响应?

只需让服务器(本例中为 API)响应 OPTIONS 请求,无需身份验证。

【讨论】:

我应该始终对 OPTIONS 请求提供相同的响应,还是应该取决于请求的资源?如果它依赖于资源,攻击者可以使用 OPTIONS 请求来发现该资源支持的服务器内容/url 和功能。也请看这个问题:***.com/questions/20805058/… 不验证 OPTION 请求是否存在安全风险? 对于那些在这里结束的人:值得使用curl -X OPTIONS http://yourdomain.example.com 来查看您的服务器对 OPTIONS 请求的响应。还要确保您完全清除浏览器缓存,Firefox 会主动缓存这些响应,因此即使您更改了服务器配置,它仍然会报告相同的错误。 这对我来说是一个非常困难的发现过程。我不知道为什么花了这么长时间才找到这个答案,但了解“阻止 cookie 标志”以及它适用于“飞行前”帮助我理解 OPTIONS 请求不会发送 cookie . 我真的很想回答@KevinMeredith 提出的问题......如果有的话,OPTIONS 请求不需要身份验证的安全风险是什么?【参考方案2】:

这是一篇旧帖子,但也许这可以帮助人们完成 CORS 问题。要完成基本授权问题,您应该避免在服务器中对 OPTIONS 请求进行授权。这是一个 Apache 配置示例。只需在您的 VirtualHost 或 Location 中添加类似的内容。

<LimitExcept OPTIONS>
    AuthType Basic
    AuthName <AUTH_NAME>
    Require valid-user
    AuthUserFile <FILE_PATH>
</LimitExcept>

【讨论】:

这是一个非常好的答案——谢谢!!!!!! --- sugest--- SetEnvIf Origin "^(.*?)$" origin_is=$0 标头始终设置 Access-Control-Allow-Origin %origin_ise env=origin_is【参考方案3】:

这对我来说很特别。我正在发送一个名为“SESSIONHASH”的标头。 Chrome 和 Opera 没问题,但 Firefox 也希望在“Access-Control-Allow-Headers”列表中使用此标头。否则,Firefox 会抛出 CORS 错误。

【讨论】:

以上是关于为啥经过身份验证的 CORS 请求的预检 OPTIONS 请求在 Chrome 中有效,但在 Firefox 中无效?的主要内容,如果未能解决你的问题,请参考以下文章

PouchDB 身份验证触发 CORS 预检请求

在 Angular 2 中使用基本身份验证预检 CORS 请求

CORS 预检请求返回带有 Windows 身份验证的 HTTP 401

对 Kong api 网关端点的基本身份验证请求出现 CORS 错误并且未找到预检

将 Spring Security(双向 SSL)配置为仅对 CORS 预检 OPTIONS 请求进行身份验证

使用 Windows 身份验证启用 CORS ASP.NET Web API 2 应用程序中的预检请求