从突破到壁垒:使用沙盒强化应用
自 CVE-2023-4863:WebP 中的堆缓冲区溢出 公开以来已过去一周多,这促使渲染 webp 图像的软件纷纷发布新版本:macOS、iOS、Chrome、Firefox 和各种 Linux 发行版都收到了更新。此前,Citizen Lab 的调查发现,一个“位于华盛顿特区的公民社会组织”使用的 iPhone 正在遭受 iMessage 中的零点击漏洞攻击。
Electron 也迅速行动起来,并在当天发布了新版本:如果您的应用程序渲染任何用户提供的内容,则应更新您的 Electron 版本 - v27.0.0-beta.2、v26.2.1、v25.8.1、v24.8.3 和 v22.3.24 都包含已修复的 libwebp 版本,这是负责渲染 webp 图像的库。
现在我们都清楚,“渲染图像”这样看似无害的操作也可能带来危险,我们想借此机会提醒大家,Electron 提供了一个进程沙盒,可以限制下一次重大攻击(无论是什么)的波及范围。
沙盒自 Electron v1 版本起就已存在,并在 v20 版本中默认启用。但我们知道,许多应用程序(尤其是那些已存在一段时间的应用程序)可能在其代码中的某个地方设置了 sandbox: false,或者设置了 nodeIntegration: true,当没有明确的 sandbox 设置时,后者同样会禁用沙盒。这是可以理解的:如果您与我们同行已久,您可能享受过将 require("child_process") 或 require("fs") 放入运行 HTML/CSS 的同一代码中的强大功能。
在我们讨论如何迁移到沙箱之前,让我们先讨论一下你为什么需要它。
沙盒为所有渲染器进程提供了严格的限制,确保无论内部发生什么,代码都在受限的环境中执行。作为一个概念,它比 Chromium 早得多,并且是所有主流操作系统提供的功能。Electron 和 Chromium 的沙盒都建立在这些系统功能之上。即使您从不显示用户生成的内容,也应考虑您的渲染器可能被破坏的可能性:像供应链攻击这样复杂的场景,以及像小 bug 这样简单的错误,都可能导致您的渲染器执行您并未完全打算让它执行的操作。
沙盒使这种情况不那么可怕:内部进程可以自由使用 CPU 周期和内存——仅此而已。进程无法写入磁盘或显示自己的窗口。在我们的 libwep 漏洞案例中,沙盒确保攻击者无法安装或运行恶意软件。事实上,在最初针对该员工 iPhone 的 Pegasus 攻击案例中,攻击者专门针对一个非沙盒化的图像进程来获取手机的访问权限,首先突破了通常被沙盒化的 iMessage 的边界。当出现像本例中的 CVE 时,您仍然需要将您的 Electron 应用程序升级到安全版本——但在此期间,攻击者造成的损害将大大减少。
将一个标准的 Electron 应用程序从 sandbox: false 迁移到 sandbox: true 是一项艰巨的任务。我知道,因为尽管我个人撰写了 Electron 安全指南 的初稿,但我自己还没有成功地迁移我的一个应用程序来使用它。本周末情况发生了变化,我建议您也这样做。

你需要解决两个问题
-
如果您在
preload脚本或实际的WebContents中使用 Node.js 代码,您需要将所有 Node.js 交互移至主进程(或,如果您喜欢,迁移到一个辅助进程)。考虑到渲染器已变得多么强大,您的绝大多数代码可能实际上不需要重构。请参阅我们关于 进程间通信 的文档。就我个人而言,我迁移了大量代码,并使用
ipcRenderer.invoke()和ipcMain.handle()进行包装,但这个过程很简单,而且很快就完成了。在这里,要注意您的 API——如果您构建了一个名为executeCodeAsRoot(code)的 API,沙盒将无法很好地保护您的用户。 -
由于启用沙箱会禁用预加载脚本中的 Node.js 集成,因此你无法再使用
require("../my-script")。换句话说,你的预加载脚本需要是单个文件。有多种方法可以做到这一点:Webpack、esbuild、parcel 和 rollup 都可以完成这项工作。我使用了 Electron Forge 出色的 Webpack 插件,同样流行的
electron-builder用户可以使用electron-webpack。
总而言之,整个过程花费了我大约四天时间——这还包括了我为了用各种方式重构我的代码而对如何驾驭 Webpack 的强大功能感到困惑的时间。
