跳至主要内容

从突破到防护:使用沙盒强化应用

·阅读 5 分钟

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 的沙盒建立在这些系统功能之上。即使您从不显示用户生成的内容,您也应该考虑您的渲染器可能受到损害的可能性:复杂的供应链攻击和简单的微小错误都可能导致您的渲染器执行您未完全预期的操作。

沙盒使这种情况的威胁大大降低:内部进程可以自由使用 CPU 周期和内存——仅此而已。进程不能写入磁盘或显示自己的窗口。在我们的 `libwep` 错误案例中,沙盒确保攻击者无法安装或运行恶意软件。事实上,在针对员工 iPhone 的原始 Pegasus 攻击中,攻击者专门瞄准了一个非沙盒化的图像进程以获取对手机的访问权限,首先突破了通常沙盒化的 iMessage 的边界。当出现像本例中的 CVE 时,您仍然需要将 Electron 应用升级到安全版本——但在此期间,攻击者可能造成的损害将大大受限。

将一个普通的 Electron 应用从 `sandbox: false` 迁移到 `sandbox: true` 是一项艰巨的任务。我知道这一点,因为尽管我亲自撰写了 Electron 安全指南 的初稿,但我仍未能将我的一个应用迁移到使用它。这周末情况有所改变,我也建议您做出改变。

Don’t be scared by the number of line changes, most of it is in package-lock.json

您需要解决两个问题

  1. 如果您在 `preload` 脚本或实际的 `WebContents` 中使用 Node.js 代码,您需要将所有这些 Node.js 交互移动到主进程(或者,如果您追求高级,可以使用一个工具进程)。鉴于渲染器现在变得多么强大,您的绝大多数代码可能并不需要真正的重构。

    请查阅我们关于进程间通信的文档。就我而言,我移动了大量代码并将其封装在 `ipcRenderer.invoke()` 和 `ipcMain.handle()` 中,但这个过程直接且迅速完成。在此处请稍微注意您的 API——如果您构建了一个名为 `executeCodeAsRoot(code)` 的 API,那么沙盒将无法为您的用户提供多少保护。

  2. 由于启用沙盒会禁用预加载脚本中的 Node.js 集成,您将无法再使用 `require("../my-script")`。换句话说,您的预加载脚本需要是单个文件。

    有多种方法可以实现这一点:Webpack、esbuild、parcel 和 rollup 都可以完成任务。我使用了 Electron Forge 出色的 Webpack 插件,同样流行的 `electron-builder` 用户可以使用 `electron-webpack`

总而言之,整个过程花了我大约四天时间——这包括我挠头研究如何驾驭 Webpack 强大功能的大量时间,因为我决定利用这个机会以许多其他方式重构我的代码。