跳转到主内容

上下文隔离

上下文隔离是什么?

上下文隔离功能将确保您的 预加载脚本 和 Electron的内部逻辑 运行在所加载的 webcontent网页 之外的另一个独立的上下文环境里。 这对安全性很重要,因为它有助于阻止网站访问 Electron 的内部组件 和 您的预加载脚本可访问的高等级权限的API 。

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

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

迁移

没有上下文隔离,从预加载脚本提供API时,经常会使用window.X = apiObject 那么现在呢?

之前: 上下文隔离禁用

在渲染进程中,预加载脚本暴露给已加载的页面 API 是一个常见的使用方式。 当上下文隔离时,您的预加载脚本可能会暴露一个常见的全局window对象给渲染进程。 此后,您可以从中添加任意的属性到预加载在脚本。

preload.js
// 上下文隔离禁用的情况下使用预加载
window.myAPI = {
doAThing: () => {}
}

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

renderer.js
// 在渲染器进程使用导出的 API
window.myAPI.doAThing()

之后:启用上下文隔离

Electron 提供一种专门的模块来无阻地帮助您完成这项工作。 The contextBridge module can be used to safely expose APIs from your preload script's isolated context to the context the website is running in. API 还可以像以前一样,从 window.myAPI 网站上访问。

preload.js
// 在上下文隔离启用的情况下使用预加载
const { contextBridge } = require('electron')

contextBridge.exposeInMainWorld('myAPI', {
doAThing: () => {}
})
renderer.js
// 在渲染器进程使用导出的 API
window.myAPI.doAThing()

请阅读 contextBridge 的文档,以全面了解其限制。 例如,您不能在 contextBridge 中暴露原型或者 Symbol。

安全事项

单单开启和使用 contextIsolation 并不直接意味着您所做的一切都是安全的。 例如,此代码是 不安全的

preload.js
// ❌ 错误使用
contextBridge.exposeInMainWorld('myAPI', {
send: ipcRenderer.send
})

它直接暴露了一个没有任何参数过滤的高等级权限 API 。 这将允许任何网站发送任意的 IPC 消息,这不会是你希望发生的。 相反,暴露进程间通信相关 API 的正确方法是为每一种通信消息提供一种实现方法。

preload.js
// ✅ 正确使用
contextBridge.exposeInMainWorld('myAPI', {
loadPreferences: () => ipcRenderer.invoke('load-prefs')
})

与Typescript一同使用

如果您正在使用 TypeScript 构建 Electron 应用程序,您需要给通过 context bridge 暴露的 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 编译器将会知晓electronAPI合适地在您的全局window对象中

renderer.ts
window.electronAPI.loadPreferences()