Content Security Policy

使用WebExtension API 开发的插件默认应用了内容安全策略(Content Security Policy, 缩写CSP)。这限制了可以加载的 <script><object> 的资源来源,并且禁止了潜在的不安全用法如 eval().

这篇文章简单地解释了CSP是什么,默认的策略是什么,这对插件来说意味着什么,以及插件如何改变默认CSP。

Content Security Policy (CSP) 是一种避免网站意外执行包含有恶意的内容的机制。网站通过使用服务端发送的HTTP头指定CSP。CSP主要关注指定各种内容的合法来源,如脚本和嵌入式插件。例如,网站可以使用它来告诉浏览器应该只执行来自网站自身的JavaScript,而不腻执行其他来源的脚本。CSP还可以指导浏览器禁止潜在危险行为,如eval()的使用。

和网页一样,插件可以加载其他来源的内容。例如浏览器的弹出窗口可以指定为一个HTML文档,它同样可以包含不同来源的JavaScript和CSS,就像一个普通的网页一样。

<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf-8">
  </head>

  <body>

    <!--Some HTML content here-->

    <!--
      Include a third-party script.
      See also https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity.
    -->
    <script
      src="https://code.jquery.com/jquery-2.2.4.js"
      integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI="
      crossorigin="anonymous">
    </script>
 
    <!-- Include my popup's own script-->
    <script src="popup.js"></script>
  </body>

</html>

和网站相比,插件可以访问特权API,因此一旦它们被恶意代码破坏,风险就更大。因此:

  • 插件默认运行在一个相当严格的安全策略下。参考 default content security policy.
  • 插件的作者可以通过使用manifest.json中的 content_security_policy 关键词改变这种默认策略,但是允许的策略仍然有一定的限制。参考 content_security_policy.

默认内容安全策略

对插件的默认内容安全策略如下:

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

这会被应用在任何没有明确在manifest.json中的content_security_policy 项设置自己的内容安全策略的插件中。它有以下几种效果:

script 和 object资源的位置

在默认CSP下你只能加载相对插件来说本地的 <script><object> 资源。例如假设插件文档中存在这样一条语句:

 <script src="https://code.jquery.com/jquery-2.2.4.js"></script>

这不会加载请求的资源:它会安静地失败,并且你所期望看到的任何来自该资源的对象都不会出现。对于这种情况有两种解决办法:

  • 下载该资源,打包进你的插件,然后引用它。

  • 使用 content_security_policy 允许你所需要的资源。

eval() 和friends

默认CSP下插件不被允许像JavaScript一样执行字符串。这意味着以下情况都被禁止:

eval("console.log('some output');");
window.setTimeout("alert('Hello World!');", 500);
var f = new Function("console.log('foo');");

内联 JavaScript

默认CSP下内联JavaScript不被执行。这不仅不允许将JavaScript直接放在 <script> 标签中间,也不允许内联事件句柄。即以下内容被禁止:

<script>console.log("foo");</script>
<div onclick="console.log('click')">Click me!</div>

如果你正在使用类似 <body onload="main()"> 的代码在页面加载时运行你的脚本,请使用监听器监听DOMContentLoaded 或者 load 代替。