内容安全策略(CSP)

为了缓解很大一部分潜在的跨站脚本问题,Chrome 浏览器的扩展程序系统引入了内容安全策略(CSP)的一般概念。这将引入一些相当严格的策略,会使扩展程序在默认情况下更加安全,并使您能够创建并强制应用一些规则,管理您的扩展程序和应用允许加载的内容类型。

大体上,CSP 以白名单/黑名单的机制对您的扩展程序加载或执行的资源起作用。为您的扩展程序定义一项合理的策略使您可以仔细考虑您的扩展程序需要的资源,并且使浏览器确保您的扩展程序只能访问指定的那些资源。这些策略提供了比您的扩展程序请求的主机权限更高的安全性,它们是额外的保护层,而不是替代品。

在网页中,这样的策略通过 HTTP 头信息或者 meta 元素定义。在 Chrome 浏览器的扩展程序系统中,这些都不是合适的方式。扩展程序的策略通过扩展程序的 manifest.json 文件定义,如下所示:

{
  ...,
  "content_security_policy": "[策略字符串写在这里]"
  ...
}

有关 CSP 语法的完整细节,请参见内容安全策略规范(英文),您还可参考 HTML5Rocks 上的内容安全策略简介(英文)这篇文章。

默认策略限制

没有定义 manifest_version(清单文件版本) 的扩展程序包没有默认的内容安全策略,而选择 manifest_version 2 的扩展程序具有如下默认的内容安全策略:

script-src 'self'; object-src 'self'

这一策略通过三种方式限制扩展程序和应用,来增强安全性:

eval 及相关函数已禁用

如下所示的代码不能工作:

alert(eval("foo.bar.baz"));
window.setTimeout("alert('hi')", 10);
window.setInterval("alert('hi')", 10);
new Function("return foo.bar.baz");

像这样对 JavaScript 字符串求值是一种常见的 XSS 攻击载体,而您应该编写如下所示的代码:

alert(foo && foo.bar && foo.bar.baz);
window.setTimeout(function() { alert('hi'); }, 10);
window.setInterval(function() { alert('hi'); }, 10);
function() { return foo && foo.bar && foo.bar.baz };

内嵌 JavaScript 代码将不会执行

内嵌 JavaScript 代码将不会执行。这一限制既禁用了内嵌的 <script>块,同时也包括内嵌的事件处理函数(例如<button onclick="...">)。

第一个限制使您不可能意外地执行任何恶意的第三方提供的脚本,彻底消除了一大部分的跨站脚本攻击。然而,这也确实要求您在编写代码时清晰地将内容与行为分开(这也是您当然应该做的吧?)。举一个例子可能会更加清楚,您可能想要编写一个包含如下内容的 popup.html,作为浏览器按钮的弹出内容

<!doctype html>
<html>
  <head>
    <title>我做的很棒的弹出内容!</title>
    <script>
      function awesome() {
        // 做些很棒的事情!
      }

      function totallyAwesome() {
        // 做些棒极了的事情!
      }

      function clickHandler(element) {
        setTimeout("awesome(); totallyAwesome()", 1000);
      }

      function main() {
        // 在这里进行初始化工作。
      }
    </script>
  </head>
  <body onload="main();">
    <button onclick="clickHandler(this)">
      单击看看会发生什么!
    </button>
  </body>
</html>

有三个地方需要修改,才能使以上代码按照您预期的方式工作:

做出这些更改后代码如下所示:

function awesome() {
  // 做些很棒的事情!
}

function totallyAwesome() {
  // 做些棒极了的事情!
}

function awesomeTask() {
  awesome();
  totallyAwesome();
}

function clickHandler(e) {
  setTimeout(awesomeTask, 1000);
}

function main() {
  // 在这里进行初始化工作。
}

// 通过监听文档的 `DOMContentLoaded` 事件在 DOM 完全加载后添加
// 事件监听器,当事件触发时向指定元素添加您自己的监听器。
document.addEventListener('DOMContentLoaded', function () {
  document.querySelector('button').addEventListener('click', clickHandler);
  main();
});
<!doctype html>
<html>
  <head>
    <title>我做的很棒的弹出内容!</title>
    <script src="popup.js"></script>
  </head>
  <body>
    <button>单击看看会发生什么!</button>
  </body>
</html>

只有本地脚本和对象资源才会加载

脚本与对象资源只能从扩展程序包中加载,而不能从范围更大的网上加载。这样确保您的扩展程序只会执行您确实允许的代码,避免任何主动的网络攻击者,恶意地重定向您对资源的请求。

不要编写依赖于外部 CDN 加载的 jQuery(或其他库)的代码,而应该考虑将特定版本的 jQuery 包含在您的扩展程序包中。即,不要:

<!doctype html>
<html>
  <head>
    <title>我做的很棒的弹出内容!</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
  </head>
  <body>
    <button>单击看看会发生什么!</button>
  </body>
</html>

而应该下载此文件,包含在您的扩展程序包中,并编写如下代码:

<!doctype html>
<html>
  <head>
    <title>我做的很棒的弹出内容!</title>
    <script src="jquery.min.js"></script>
  </head>
  <body>
    <button>单击看看会发生什么!!</button>
  </body>
</html>

放宽默认策略

内嵌脚本

没有办法放宽限制,允许执行内嵌 JavaScript 代码。特别地,设置包含 unsafe-inline 的脚本策略不会生效。

远程脚本

如果您需要某些外部 JavaScript 代码或对象资源,您可以在有限的程度上放宽策略,将安全来源的可接受脚本加入白名单。我们希望确保以扩展程序提升的权限加载的可执行资源一定是您预期的,而没有被主动的网络攻击者替换。由于中间人攻击非常普遍,并且通过 HTTP 无法检测到,这些来源不会被接受。目前,我们允许将来源为以下协议的资源加入白名单:HTTPSchrome-extensionchrome-extension-resource

为了方便开发,我们也允许将那些通过 HTTP 从本地计算机的服务器上加载的资源列入白名单,您可以将 http://127.0.0.1http://localhost 任意端口上的脚本和对象来源加入白名单。

对于通过 HTTP 加载资源的限制仅仅适用于直接执行的那些资源,您仍然可以,例如,向您希望使用的任何来源发起 XMLHttpRequest 连接,默认策略不会以任何方式限制 connect-src 或者其他任何 CSP 指示符。

允许通过 HTTPS 加载来自 example.com 的脚本资源的放宽策略定义如下所示:

"content_security_policy": "script-src 'self' https://example.com; object-src 'self'"

注意 script-srcobject-src 都由这一策略定义,Chrome 浏览器不会接受不将这些值限制为(至少)'self'的策略。

利用 Google Analytics(分析)是这一种策略定义的典型例子,这样的情况很常见,所以我们在利用 Google Analytics(分析)追踪事件的示例扩展程序中提供了简单例子,并在简明教程中提供了更多详情。

JavaScript 求值

阻止 eval 及类似构造,像 setTimeout(String)setInterval(String) 以及 new Function(String) 的策略也可以通过向您的策略添加 unsafe-eval 来放松:

"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"

然而我们强烈建议您不要这么做。这些函数是臭名昭著的 XSS 攻击载体。

使用更严格的策略

您当然也可以以带来更多不便为代价,使用您的扩展程序允许的更严格的策略,来增强安全性。例如,要指定您的扩展程序只能从自己的包中加载所有类型(如图片等)的资源,可以使用这样的策略:default_src 'self'Mappy 示例扩展程序就是使用的策略比默认设置更加严格的例子。