chrome 扩展中的 Access-Control-Allow-Origin 错误
Posted
技术标签:
【中文标题】chrome 扩展中的 Access-Control-Allow-Origin 错误【英文标题】:Access-Control-Allow-Origin error in a chrome extension 【发布时间】:2012-02-26 23:28:30 【问题描述】:我有一个 chrome 扩展程序,它以一种特殊的方式监控浏览器,将一些数据发送到网络服务器。在当前配置中,这是本地主机。所以内容脚本包含这样的代码:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(data)...
xhr.open('GET', url, true);
xhr.send();
其中 url 参数是 'http://localhost/ctrl?params'(或 http://127.0.0.1/ctrl?params - 没关系)。
清单文件包含跨站点请求的所有必要权限。
该扩展程序在大多数网站上都可以正常工作,但在一个网站上我收到错误:
XMLHttpRequest cannot load http://localhost/ctrl?params. Origin http://www.thissite.com is not allowed by Access-Control-Allow-Origin.
我尝试了这里提出的几种权限(*://*/*
、http://*/*
和 <all_urls>
),但没有人帮助解决问题。
所以,问题是这个特定网站可能有什么问题(显然可能有其他网站有类似的不当行为,我想知道这种情况的性质),以及如何解决错误?
【问题讨论】:
您能否更具体地说明您在哪个网站上看到此错误?它可能正在使用内容安全策略 (dvcs.w3.org/hg/content-security-policy/raw-file/tip/…) 来阻止加载数据。 这里是网站 - www.wix.com。 米海,你的意思是网站的“内容安全策略”优先于扩展权限吗?那么我们如何才能编写一个可靠的扩展呢?有解决办法吗? 在来自该站点的特定 HTTP 标头中,我只看到以下标头:X-Content-Type-Options: nosniff
和 X-XSS-Protection: 1; mode=block
。他们中的任何一个是否违反了扩展程序的权限?我在网上没有找到相关信息。
这些标题不应该影响您的扩展程序的行为(我也没有在 www.wix.com 响应中看到它们,它们是针对网站的特定子页面的)。是否可以共享您的扩展程序(和/或创建一个仅复制此问题的最小版本)?
【参考方案1】:
(tl;dr:在答案末尾查看两种可能的解决方法)
这是发生的一系列事件,导致您看到的行为:
-
http://www.wix.com/ 开始加载
它有一个
<script>
标签,可以异步加载Facebook Connect脚本:
(功能()
var e = document.createElement('script');
e.type = '文本/javascript';
e.src = document.location.protocol +
'//connect.facebook.net/en_US/all.js';
e.async = true;
document.getElementById('fb-root').appendChild(e);
());
一旦加载 wix.com 页面的 html(但不包括资源,包括 Facebook Connect 脚本),DOMContentLoaded
事件就会触发。由于您的内容脚本使用"run_at" : "document_end"
,因此此时它会被注入并运行。
您的内容脚本运行以下代码(据我所知,它希望在load
事件触发后完成大部分工作):
window.onload = 函数()
// 最终执行跨域 XMLHttpRequest 的代码
;
Facebook Connect 脚本加载,它有自己的load
事件处理程序,它使用以下 sn-p 添加:
(功能()
var oldonload=window.onload;
window.onload=函数()
// 运行新的加载代码
如果(旧负载)
if(typeof oldonload=='string')
评估(旧负载);
别的
旧负载();
;
)();
(这是第一个关键部分)由于您的脚本设置了 onload
属性,oldonload
是您脚本的加载处理程序。
最终,所有资源都被加载,load
事件处理程序被触发。
Facebook Connect 的load
处理程序运行,它运行自己的代码,然后调用oldonload
。 (这是第二个关键部分)由于页面正在调用您的 load
处理程序,因此它不是在脚本的 isolated world 中运行它,而是在页面的“主要世界”中运行。只有脚本的孤立世界具有跨域XMLHttpRequest
访问权限,因此请求失败。
要查看对此的简化测试用例,请参阅 this page(模仿 http://www.wix.com),它会加载 this script(模仿 Facebook Connect)。我还提供了content script 和extension manifest 的简化版本。
您的load
处理程序最终在“主世界”中运行这一事实很可能是Chrome bug 87520 的表现(该错误具有安全隐患,因此您可能看不到它)。
有两种方法可以解决这个问题:
-
除了使用
"run_at" : "document_end"
和load
事件处理程序之外,您还可以使用默认运行时间(document_idle
,在文档加载后),并让您的代码内联运行。
不要通过设置window.onload
属性来添加load
事件处理程序,而是使用window.addEventListener('load', func)
。这样一来,您的事件处理程序将不会对 Facebook Connect 可见,因此它将在内容脚本的孤立世界中运行。
【讨论】:
你是天才。非常感谢。 感谢您的详细解答!【参考方案2】:您看到的访问控制来源问题可能体现在响应的标头中(不受您的控制),而不是请求(在您的控制之下)。
Access-Control-Allow-Origin 是CORS 的策略,在标头中设置。例如,使用 php,您可以使用一组如下所示的标头来启用 CORS:
header('Access-Control-Allow-Origin: http://blah.com');
header('Access-Control-Allow-Credentials: true' );
header('Access-Control-Allow-Headers: Content-Type, Content-Disposition, attachment');
如果服务器在此标头中设置特定来源,那么您的 Chrome extension 遵循指令以允许仅来自该域的跨域(POST?)请求。
【讨论】:
如果能得到确认,那么它看起来像 Chrome 中的一个错误,因为扩展权限应该允许跨域请求。我认为这种情况通过webRequest
进行“手动”http-header 处理是不正常的。以上是关于chrome 扩展中的 Access-Control-Allow-Origin 错误的主要内容,如果未能解决你的问题,请参考以下文章