跨站 XMLHttpRequest

普通网页可以通过 XMLHttpRequest 对象向远程服务器发送和接收数据,但是它们受到相同来源策略的限制。扩展程序不受这一限制,只要首先请求跨站权限,扩展程序就可以与来源范围外的远程服务器通信。

扩展程序的来源

每一个运行中的扩展程序在它自己的安全来源中存在,如果不请求额外的权限,扩展程序只能使用 XMLHttpRequest 获取自己的资源。例如,如果扩展程序包含一个名为 config.json 的 JSON 配置文件,位于 config_resources 文件夹中,扩展程序可以像这样获取文件内容:

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = handleStateChange; // 在其他地方实现。
xhr.open("GET", chrome.extension.getURL('/config_resources/config.json'), true);
xhr.send();

如果扩展程序尝试使用除了它自己的安全来源,例如 http://www.google.com,除非扩展程序已经请求了相应的跨站权限,浏览器会禁止这一请求。

请求跨站权限

通过向清单文件permissions 部分添加主机或主机匹配表达式,扩展程序可以请求访问位于自己的来源外的远程服务器。

{
  "name": "我的扩展程序",
  ...
  "permissions": [
    "http://www.google.com/"
  ],
  ...
}

跨站权限值可以是完整的主机名,例如:

  • "http://www.google.com/"
  • "http://www.gmail.com/"

或者也可以是匹配表达式,例如:

  • "http://*.google.com/"
  • "http://*/"

"http://*/" 这一匹配表达式允许所有可及域名的访问。注意,这里的匹配表达式和内容脚本匹配表达式类似,但是主机后的任何路径信息都将被忽略。

同时注意访问权限是通过主机和协议同时授予的,如果扩展程序需要指定某个主机安全和非安全的 HTTP 访问,必须分开声明权限:

"permissions": [
  "http://www.google.com/",
  "https://www.google.com/"
]

安全性考虑

当使用通过 XMLHttpRequest 获取的资源时,您的后台网页应该小心,以免跨站脚本攻击。特别地,避免使用如下一些危险的 API:

var xhr = new XMLHttpRequest();
xhr.open("GET", "http://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    // 警告!可能会执行恶意脚本!
    var resp = eval("(" + xhr.responseText + ")");
    ...
  }
}
xhr.send();
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    // 警告!可能会插入恶意脚本
    document.getElementById("resp").innerHTML = xhr.responseText;
    ...
  }
}
xhr.send();

因此,您应该首选更安全的不运行脚本的 API:

var xhr = new XMLHttpRequest();
xhr.open("GET", "http://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    // JSON.parse 不执行攻击者的脚本。
    var resp = JSON.parse(xhr.responseText);
  }
}
xhr.send();
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    // innerText 不会让攻击者插入 HTML 元素。
    document.getElementById("resp").innerText = xhr.responseText;
  }
}
xhr.send();

另外,当您通过 HTTP 接收资源时尤其要小心。如果您的扩展程序在不安全的网络环境中使用,网络攻击者(即中间人攻击)可以修改响应,甚至可能攻击您的扩展程序。请尽可能地在可能的情况下首选 HTTPS。

与内容安全策略的关系

如果您在您的清单文件中添加 content_security_policy 属性修改了应用或扩展程序默认的内容安全策略,您需要确保您需要连接的所有主机都受到允许。尽管默认策略不限制连接到哪些主机,当您显式添加 connect-srcdefault-src 指示符时要特别注意。