跳至主要内容

突破障碍:使用沙箱增强应用安全性

·阅读时长 4 分钟

距离 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. 由于启用沙箱会禁用 preload 脚本中的 Node.js 集成,您将不能再使用 require("../my-script")。换句话说,您的 preload 脚本需要是一个单独的文件。

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

总而言之,整个过程花了我大约四天时间——这包括很多抓耳挠腮地思考如何驾驭 Webpack 强大的功能,因为我决定借此机会在许多其他方面重构我的代码。