上下文隔离
它是什么?
上下文隔离是一项功能,可确保您的 preload
脚本和 Electron 的内部逻辑在与您加载到 webContents
中的网站不同的上下文中运行。 这对于安全目的非常重要,因为它有助于防止网站访问 Electron 内部组件或您的预加载脚本有权访问的强大 API。
这意味着您的预加载脚本有权访问的 window
对象实际上与网站有权访问的对象不同。 例如,如果您在预加载脚本中设置 window.hello = 'wave'
并且启用了上下文隔离,则如果网站尝试访问 window.hello
,它将是未定义的。
自 Electron 12 以来,默认情况下已启用上下文隔离,并且它是所有应用程序的推荐安全设置。
迁移
如果没有上下文隔离,我过去使用
window.X = apiObject
从我的预加载脚本中提供 API。 现在该怎么办?
之前:禁用上下文隔离
从预加载脚本向渲染器进程中加载的网站公开 API 是一种常见的用例。 禁用上下文隔离后,您的预加载脚本将与渲染器共享一个公共的全局 window
对象。 然后,您可以将任意属性附加到预加载脚本
// preload with contextIsolation disabled
window.myAPI = {
doAThing: () => {}
}
然后,doAThing()
函数可以直接在渲染器进程中使用
// use the exposed API in the renderer
window.myAPI.doAThing()
之后:启用上下文隔离
Electron 中有一个专用模块可以帮助您以一种轻松的方式做到这一点。 contextBridge
模块可用于将 API 从您的预加载脚本的隔离上下文中安全地公开到网站运行的上下文中。 该 API 也可以像以前一样从网站上的 window.myAPI
访问。
// preload with contextIsolation enabled
const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('myAPI', {
doAThing: () => {}
})
// use the exposed API in the renderer
window.myAPI.doAThing()
请阅读上面链接的 contextBridge
文档以充分了解其局限性。 例如,您无法通过桥接发送自定义原型或符号。
安全注意事项
仅仅启用 contextIsolation
和使用 contextBridge
并不意味着您所做的一切都是安全的。 例如,此代码是不安全的。
// ❌ Bad code
contextBridge.exposeInMainWorld('myAPI', {
send: ipcRenderer.send
})
它直接公开了一个强大的 API,没有任何类型的参数过滤。 这将允许任何网站发送任意 IPC 消息,您不希望这是可能的。 公开基于 IPC 的 API 的正确方法是为每个 IPC 消息提供一个方法。
// ✅ Good code
contextBridge.exposeInMainWorld('myAPI', {
loadPreferences: () => ipcRenderer.invoke('load-prefs')
})
与 TypeScript 一起使用
如果您正在使用 TypeScript 构建 Electron 应用程序,您将需要向通过上下文桥公开的 API 添加类型。 渲染器的 window
对象不会有正确的类型,除非您使用 声明文件扩展这些类型。
例如,给定此 preload.ts
脚本
contextBridge.exposeInMainWorld('electronAPI', {
loadPreferences: () => ipcRenderer.invoke('load-prefs')
})
您可以创建一个 interface.d.ts
声明文件并全局增强 Window
接口
export interface IElectronAPI {
loadPreferences: () => Promise<void>,
}
declare global {
interface Window {
electronAPI: IElectronAPI
}
}
这样做将确保 TypeScript 编译器在渲染器进程中编写脚本时知道全局 window
对象上的 electronAPI
属性
window.electronAPI.loadPreferences()