首页 / Electron 入门教程 / 上下文隔离与沙箱

Electron 入门教程

上下文隔离与沙箱

本教程共 45 篇 · 第 10 篇 · 更新于 2026-08-03

ElectroncontextIsolationsandboxnodeIntegration安全模型

10. 上下文隔离与沙箱

学完 preload,你必须紧接着理解它背后的安全模型。否则你写的代码也许能跑,却可能把整个应用变成漏洞。本章讲清三个开关:contextIsolationsandboxnodeIntegration,以及它们为什么这样组合。

本节目标

  • 理解 contextIsolation: true 到底隔离了什么。
  • 明白 sandbox 限制了渲染进程哪些系统权限。
  • 知道 nodeIntegration: false 为何是默认且必要的。
  • 看清 Electron 安全模型的整体设计意图。
  • 记住三条红线写法,避免写出不安全的配置。

10-1 contextIsolation 隔离了什么

contextIsolation(上下文隔离)保证:你的 preload 脚本、Electron 内部逻辑,和网页加载的内容,运行在彼此分离的 JavaScript 上下文里。

没有它时,preload 和网页共用同一个 window,preload 挂什么,网页就能摸什么。一旦网页被注入恶意脚本,preload 持有的强大能力就直接暴露了。

开启后,preload 的 window 与网页的 window 是两个不同的对象。preload 里设的属性,网页读不到;网页里的变量,也污染不到 preload。两者之间的唯一合法通道,就是 contextBridge

// 主进程 main.js
const { BrowserWindow } = require('electron/main')
const path = require('node:path')

const win = new BrowserWindow({
  webPreferences: {
    contextIsolation: true,          // 隔离上下文(v43 默认即 true)
    nodeIntegration: false,          // 不向渲染进程暴露 Node.js
    preload: path.join(__dirname, 'preload.js')
  }
})

提示:自 Electron 12 起 contextIsolation 默认就是 true。你通常不必显式写,但写上更清晰,也防止有人误改成 false。请牢记:绝不要把它设成 false

10-2 sandbox 限制了什么

沙箱(sandbox)是 Chromium 的核心安全机制:被沙箱的进程只能用 CPU 和内存,访问文件系统、网络、系统调用等特权操作,都要委托给更有权限的进程去做。

Electron 里,除主进程外的大多数进程(渲染进程、GPU、网络服务等)都可被沙箱化。从 Electron 20 起,渲染进程默认开启沙箱。被沙箱的渲染进程表现得和普通 Chrome 渲染进程一样,不再初始化 Node.js 环境。

// 主进程 main.js
const win = new BrowserWindow({
  webPreferences: {
    sandbox: true   // 开启沙箱(通常无需显式写,默认即开启)
  }
})

沙箱和 Node.js 集成是绑定的:一旦你设 nodeIntegration: true,渲染进程的沙箱会自动关闭。换句话说,给渲染进程 Node 能力,就要以牺牲沙箱为代价。所以除非万不得已,不要开 nodeIntegration

10-3 nodeIntegration 为何必须关

nodeIntegration 控制渲染进程能否直接使用 Node.js(比如 requireprocessfs)。开启后,网页里的脚本就能读写文件、执行系统命令。

这非常危险。网页内容可能来自远程,或包含第三方脚本,一旦其中混入恶意代码,它就能用 Node 能力做任意破坏。所以 Electron 默认 nodeIntegration: false,这是合理且必要的。

// preload.js(Preload 脚本)
// 即使在 preload 里,也只暴露最小能力,而非整个 Node
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('electronAPI', {
  readConfig: () => ipcRenderer.invoke('read-config')
})

注意:渲染进程需要文件读取能力时,不要开 nodeIntegration 让它自己读,而是走 IPC 让主进程代劳。主进程有完整 Node 环境,且你能在 ipcMain.handle 里做权限校验。

10-4 三者如何配合成安全模型

把三项放到一起看,逻辑就清楚了:

contextIsolation: true 负责在 preload 与网页之间划出边界。nodeIntegration: false 让网页拿不到 Node 能力。sandbox: true 则把渲染进程的系统权限压到最低。

三层叠在一起,就是所谓的纵深防御。哪怕某个页面真被注入了脚本,它也只能在自己的上下文里折腾,既跨不过隔离摸到 preload,也没有直接调用系统的入口。

preload 于是成了唯一的「受控出口」:它能被沙箱裁剪后的 require 引入少量模块,再通过 contextBridge 把封装好的函数交给网页。渲染侧拿到的能力有多大,完全由你在这一层决定。

// 主进程:安全的整体偏好配置
const win = new BrowserWindow({
  webPreferences: {
    contextIsolation: true,
    nodeIntegration: false,
    sandbox: true,
    preload: path.join(__dirname, 'preload.js')
  }
})

实际写代码时,这三项往往不用全写——因为 v43 默认值本就如此。但理解它们为何是默认值,能帮你在调试或排查时做出正确判断。

10-5 沙箱下 preload 能用的模块

需要强调:沙箱化后,preload 的 require 是个被裁剪的 polyfill,只能引入一小部分模块。根据官方文档,可用的包括:

  • Electron 渲染端模块:contextBridgecrashReporteripcRenderernativeImagewebFramewebUtils
  • Node.js 模块:eventstimersurl(以及对应的 node: 命名空间导入)。
  • 全局 polyfill:BufferprocessclearImmediatesetImmediate

这也解释了为什么 preload 不能 require 任意 npm 包:它的运行环境本就被刻意限制了。复杂逻辑应放进主进程,preload 只做「桥」。

// preload.js(Preload 脚本,沙箱下可用)
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('electronAPI', {
  sendLog: (msg) => ipcRenderer.send('log', msg)
})

10-6 全局开启沙箱与禁用开关

如果你想强制所有渲染进程都沙箱化,可以在 appready 事件之前调用 app.enableSandbox()。这会覆盖个别窗口的 sandbox: false

// 主进程 main.js
const { app } = require('electron/main')

app.enableSandbox() // 必须在 ready 之前调用

app.whenReady().then(() => {
  // 即使某处写了 sandbox:false,也会被覆盖
  const win = new BrowserWindow()
  win.loadFile('index.html')
})

反过来,个别窗口想关沙箱,用 sandbox: false。但官方明确说:对大多数应用,沙箱是最佳选择;只有在与沙箱不兼容的场景(比如渲染进程要用原生 Node 模块)才考虑关闭,且要清楚这会带来安全风险。

还有个容易混淆的点:sandbox: false 只是关掉了沙箱,并不会自动打开 Node 集成。渲染进程能否用 require,仍由 nodeIntegration 决定。两个开关各管各的,别把它们当成同一件事。

另外,命令行参数 --no-sandbox 会关掉包括工具进程在内的所有沙箱。它只适合本地排查问题时临时用,生产环境绝不要带上。

10-7 v43 的默认安全配置

把上面三项放回 BrowserWindowwebPreferences 看,v43 的默认值已经足够安全:contextIsolationtruenodeIntegrationfalse、渲染进程默认开启沙箱。也就是说,哪怕你什么都不写,新建的窗口也是隔离且沙箱的。

这带来一个实践建议:不要为了「让示例跑通」而把这三个值改回不安全的状态。如果某段代码只在 nodeIntegration: true 时才工作,正确的修法是改写代码,让它走 preload + IPC,而不是降低安全等级去迁就旧写法。

若你确实需要确认当前窗口的安全配置,可以在 preload 或主进程打印相关偏好。但更重要的是建立习惯:把安全默认值当成底线,任何偏离都要有充分理由,并且只作用于最小范围。这样应用的攻击面才能被稳稳控制住。

常见误区

  • 误区一:为了省事开 nodeIntegration: true。这会关掉沙箱、暴露 Node,是高危写法。
  • 误区二:把 contextIsolation 设成 false 来「让 preload 直接挂 window」。请用 contextBridge 替代。
  • 误区三:以为 preload 能 require 任意包。沙箱下 require 被裁剪,复杂依赖走主进程。

小结

contextIsolationsandboxnodeIntegration 共同构成 Electron 的纵深防御。隔离划出上下文边界,沙箱收走系统权限,而关掉 Node 集成堵住的是危害最大的那个后门。

这三项在 v43 都默认安全。写代码时保持默认值就好,只在有充分理由时谨慎调整,并把影响范围限制在单个窗口内。