Using eval in Chrome extensions(在 Chrome 扩展中使用 eval)

警告

此页面直接从 MV2 文档集迁移而来。它尚未经过验证是否符合 Manifest V3。

Chrome 的扩展系统执行相当严格的默认内容安全策略 (CSP)。策略限制很简单:必须将脚本移到单独的 JavaScript 文件外,必须将内联事件处理程序转换为使用 addEventListener,并且禁用 eval()

然而,我们认识到,各种库使用 eval() 和类似 eval 的构造,例如 new Function() 来优化性能和简化表达。模板库特别容易采用这种实现方式。虽然有些(如 Angular.js)支持开箱即用的 CSP,但许多流行的框架尚未更新为与扩展的 eval-less 世界兼容的机制。因此,事实证明,取消对该功能的支持比开发人员预期的问题更多

本文档介绍了沙盒作为一种安全机制,可以在不影响安全性的情况下将这些库包含在您的项目中。

# Why sandbox?

eval 在扩展中很危险,因为它执行的代码可以访问扩展的高权限环境中的所有内容。大量强大的 chrome.*API 可用,它们可能会严重影响用户的安全和隐私;简单的数据泄露是我们最不担心的。提供的解决方案是一个沙箱,其中 eval 可以在不访问扩展数据或扩展的高价值 API 的情况下执行代码。没有数据,没有 API,没问题。

我们通过将扩展包中的特定 HTML 文件列为被沙箱化来实现这一点。每当加载沙盒页面时,它将被移动到唯一的来源(unique origin),并且将被拒绝访问 chrome.* API。如果我们通过 iframe 将此沙盒页面加载到我们的扩展程序中,我们可以向它传递消息,让它以某种方式对这些消息采取行动,然后等待它向我们传回结果。这种简单的消息传递机制为我们提供了将 eval 驱动的代码安全地包含在我们的扩展工作流中所需的一切。

# Creating and using a sandbox.(创建和使用沙箱。)

如果您想直接研究代码,请获取沙盒示例扩展并开始使用。这是一个构建在 Handlebars 模板库之上的小型消息传递 API 的工作示例,它应该为您提供开始所需的一切。对于那些想要更多解释的人,让我们在这里一起浏览该示例。

# List files in manifest(列出清单中的文件)

每个应该在沙箱内运行的文件都必须通过添加沙箱属性在扩展清单中列出。这是一个关键步骤,很容易忘记,所以请仔细检查您的沙盒文件是否列在清单中.在此示例中,我们对巧妙命名为“sandbox.html”的文件进行了沙盒处理。清单条目如下所示:

{
  ...,
  "sandbox": {
     "pages": ["sandbox.html"]
  },
  ...
}

# Load the sandboxed file(加载沙盒文件)

为了对沙盒文件做一些有趣的事情,我们需要将它加载到扩展代码可以解决的上下文中。在这里,sandbox.html 已通过 iframe 加载到扩展的事件页面 (eventpage.html) 中。eventpage.js 包含的代码通过在页面上查找 iframe 并在其 contentWindow 上执行 postMessage 方法,在单击浏览器操作时将消息发送到沙箱。消息是一个包含两个属性的对象:contextcommand。稍后我们将深入探讨两者。

chrome.browserAction.onClicked.addListener(function() {
 var iframe = document.getElementById('theFrame');
 var message = {
   command: 'render',
   context: {thing: 'world'}
 };
 iframe.contentWindow.postMessage(message, '*');
});

有关 postMessage API 的一般信息,请查看 MDN 上的 postMessage documentation on MDN 文档。它相当完整,值得一读。特别要注意的是,数据只有在可序列化的情况下才能来回传递。例如,函数不是。

# Do something dangerous(危险的事)

当加载 sandbox.html 时,它会加载 Handlebars 库,并按照 Handlebars 建议的方式创建和编译内联模板:

<script src="handlebars-1.0.0.beta.6.js"></script>
<script id="hello-world-template" type="text/x-handlebars-template">
  <div class="entry">
    <h1>Hello, !</h1>
  </div>
</script>
<script>
  var templates = [];
  var source = document.getElementById('hello-world-template').innerHTML;
  templates['hello'] = Handlebars.compile(source);
</script>

这不会失败!尽管 Handlebars.compile 最终使用了 new Function,但事情完全按预期工作,我们最终在 templates['hello'] 中得到了一个编译模板。

# Pass the result back(将结果传回)

我们将通过设置接收来自事件页面的命令的消息侦听器来使该模板可供使用。我们将使用传入的命令来确定应该做什么(您可以想象做的不仅仅是渲染;也许创建模板?也许以某种方式管理它们?),并且上下文将直接传递到模板中进行渲染。呈现的 HTML 将被传递回事件页面,以便扩展程序稍后可以使用它做一些有用的事情:

<script>
  window.addEventListener('message', function(event) {
    var command = event.data.command;
    var name = event.data.name || 'hello';
    switch(command) {
      case 'render':
        event.source.postMessage({
          name: name,
          html: templates[name](event.data.context)
        }, event.origin);
        break;

      // case 'somethingElse':
      //   ...
    }
  });
</script>

回到事件页面,我们将收到这条消息,并对我们传递的 html 数据做一些有趣的事情。在这种情况下,我们将仅通过桌面通知将其回显,但完全有可能将此 HTML 安全地用作扩展程序 UI 的一部分。通过innerHTML 插入它不会带来重大的安全风险,因为即使通过一些巧妙的攻击完全破坏沙盒代码,也无法将危险的脚本或插件内容注入高权限扩展上下文。

这种机制使模板变得简单明了,但它当然不限于模板。任何在严格的内容安全策略下不能开箱即用的代码都可以被沙盒化;事实上,将正确运行的扩展组件沙箱化通常很有用,以便将程序的每个部分限制为正确执行所需的最小权限集。来自 Google I/O 2012 的编写安全 Web 应用程序和 Chrome 扩展演示给出了这些技术的一些很好的例子,值得您花 56 分钟的时间。

By.一粒技术服务

results matching ""

    No results matching ""