铬扩展中的访问控制 - 允许 - 来源错误

问题描述:

我有一个Chrome扩展,以特殊的方式监视浏览器,发送一些数据到Web服务器。在当前配置中,这是本地主机。因此,内容脚本包含这样的代码:铬扩展中的访问控制 - 允许 - 来源错误

var xhr = new XMLHttpRequest(); 
xhr.onreadystatechange = function(data)... 
xhr.open('GET', url, true); 
xhr.send(); 

其中URL参数是 'http://本地主机/ 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>),但没有人帮助解决问题。

所以,问题是这个特定网站可能会出现什么问题(显然有可能是其他网站有类似的不当行为,我想知道这种性质),以及如何解决这个错误?

+0

你能更具体地说明哪个网站你看到这个错误吗?它可能使用内容安全策略(https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html)来防止数据被加载。 – 2012-02-05 01:23:45

+0

这里是网站 - www.wix.com。 – Stan 2012-02-05 08:47:01

+0

Mihai,你是说网站的“内容安全策略”优先于扩展权限?那么我们怎么能写一个可靠的扩展呢?有没有解决方法? – Stan 2012-02-05 08:50:05

(TL;博士:看到两个可能的解决方法在回答结束)

这是一系列发生的事件,这将导致您看到的行为:

  1. HTTP: //www.wix.com/开始加载
  2. 它有一个<script>标签异步加载Facebook连接的脚本:
     
    (function() { 
        var e = document.createElement('script'); 
        e.type = 'text/javascript'; 
        e.src = document.location.protocol + 
        '//connect.facebook.net/en_US/all.js'; 
        e.async = true; 
        document.getElementById('fb-root').appendChild(e); 
    }()); 
    
  3. 一旦HTML(而不是资源,我包括Facebook连接脚本)加载wix.com页面时,将触发DOMContentLoaded事件。由于您的内容脚本使用"run_at" : "document_end",因此此时它会被注入并运行。
  4. 您的内容脚本运行下面的代码(是最好的,我可以告诉大家,它想做load事件触发后,其大部分工作):
     
    window.onload = function() { 
        // code that eventually does the cross-origin XMLHttpRequest 
    }; 
    
  5. 的Facebook连接的脚本加载,它有它自己load事件处理程序,它在这个片段中补充说:
     
    (function() { 
        var oldonload=window.onload; 
        window.onload=function(){ 
        // Run new onload code 
        if(oldonload) { 
         if(typeof oldonload=='string') { 
         eval(oldonload); 
         } else { 
         oldonload(); 
         } 
        } 
        }; 
    })(); 
    
    (这是第一个关键部分)因为你的脚本设置onload属性,oldonload是脚本的加载处理。
  6. 最终,所有资源都会被加载,并且会触发load事件处理程序。
  7. Facebook Connect的load处理程序运行,它运行自己的代码,然后调用oldonload。 (这是第二个关键部分)由于该页面正在调用load处理函数,因此它不会在脚本的isolated world中运行,而会在页面的“主要世界”中运行。只有脚本的隔离世界存在跨源XMLHttpRequest访问权限,所以请求失败。

若要查看此简化测试情况下,看到this page(其模拟物http://www.wix.com),它加载this script(其模拟Facebook连接)。我也提出了content scriptextension manifest的简化版本。

您的load处理程序最终在“主要世界”中运行的事实很可能是Chrome bug 87520(该错误具有安全隐患,因此您可能无法看到它)的体现。

有两种方法来解决此问题:

  1. 而不是使用"run_at" : "document_end"和一个load事件处理程序,你可以使用默认的运行时间(document_idle,文件加载后),只是有你的代码运行一致。
  2. 而不是通过设置window.onload属性添加load事件处理程序,请使用window.addEventListener('load', func)。这样你的事件处理程序就不会被Facebook Connect看到,所以它会在内容脚本的孤立世界中运行。
+2

你是天才。非常感谢。 – Stan 2012-02-12 19:10:06

+0

感谢您的详细解答! – Shaihi 2012-12-17 23:00:22

您看到的访问控制源问题很可能会显示在响应的标题(不受控制)中,而不是请求(在您的控制之下)。

访问控制 - 允许来源是设置在标题中的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?)请求。

+0

如果确认,那么它看起来像Chrome中的一个错误,因为扩展权限应该允许跨源请求。我认为这种情况通过'webRequest'涉及“手动”http头处理是不正常的。 – Stan 2012-02-10 07:23:10