跳到主要内容

上下文隔离

它是什么?

上下文隔离是一项功能,它确保你的 preload 脚本和 Electron 的内部逻辑在与你加载到 webContents 中的网站不同的上下文中运行。这对于安全目的非常重要,因为它有助于防止网站访问 Electron 内部组件或你的预加载脚本可以访问的强大 API。

这意味着你的预加载脚本可以访问的 window 对象实际上与网站可以访问的对象是不同的对象。例如,如果你在预加载脚本中设置 window.hello = 'wave' 并且启用了上下文隔离,则当网站尝试访问它时,window.hello 将是 undefined。

自 Electron 12 以来,默认已启用上下文隔离,并且它是所有应用程序的推荐安全设置。

迁移

在没有上下文隔离的情况下,我过去常常使用 window.X = apiObject 从我的预加载脚本中提供 API。现在怎么办?

之前:上下文隔离禁用

将 API 从预加载脚本公开到渲染进程中加载的网站是一种常见的用例。禁用上下文隔离后,你的预加载脚本将与渲染器共享一个公共的全局 window 对象。然后,你可以将任意属性附加到预加载脚本

preload.js
// preload with contextIsolation disabled
window.myAPI = {
doAThing: () => {}
}

然后可以在渲染进程中直接使用 doAThing() 函数

renderer.js
// use the exposed API in the renderer
window.myAPI.doAThing()

之后:上下文隔离启用

Electron 中有一个专用模块可以帮助你以一种无痛的方式做到这一点。 contextBridge 模块可用于将 API 从预加载脚本的隔离上下文安全地公开到网站运行的上下文中。该 API 也将像以前一样在网站上的 window.myAPI 上访问。

preload.js
// preload with contextIsolation enabled
const { contextBridge } = require('electron')

contextBridge.exposeInMainWorld('myAPI', {
doAThing: () => {}
})
renderer.js
// use the exposed API in the renderer
window.myAPI.doAThing()

请阅读上面链接的 contextBridge 文档以充分了解其限制。 例如,你不能通过桥发送自定义原型或符号。

安全注意事项

仅仅启用 contextIsolation 并使用 contextBridge 并不意味着你所做的一切都是安全的。例如,这段代码是不安全的。

preload.js
// ❌ Bad code
contextBridge.exposeInMainWorld('myAPI', {
send: ipcRenderer.send
})

它直接公开了一个强大的 API,没有任何参数过滤。这将允许任何网站发送任意 IPC 消息,这是你不想发生的。公开基于 IPC 的 API 的正确方法是为每个 IPC 消息提供一个方法。

preload.js
// ✅ Good code
contextBridge.exposeInMainWorld('myAPI', {
loadPreferences: () => ipcRenderer.invoke('load-prefs')
})

与 TypeScript 一起使用

如果你正在使用 TypeScript 构建你的 Electron 应用程序,你将需要向通过上下文桥公开的 API 添加类型。除非你使用声明文件扩展类型,否则渲染器的 window 对象将没有正确的类型。

例如,给定此 preload.ts 脚本

preload.ts
contextBridge.exposeInMainWorld('electronAPI', {
loadPreferences: () => ipcRenderer.invoke('load-prefs')
})

你可以创建一个 interface.d.ts 声明文件并全局增强 Window 接口

interface.d.ts
export interface IElectronAPI {
loadPreferences: () => Promise<void>,
}

declare global {
interface Window {
electronAPI: IElectronAPI
}
}

这样做将确保 TypeScript 编译器在你的渲染进程中编写脚本时知道全局 window 对象上的 electronAPI 属性

renderer.ts
window.electronAPI.loadPreferences()