跳转至

上下文隔离

它是什么?

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

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

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

迁移

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

之前:上下文隔离已禁用

将 API 从 preload 脚本暴露给渲染进程中加载的网站是一个常见用例。在禁用上下文隔离时,你的 preload 脚本会与渲染进程共享一个全局 window 对象。然后你可以向 preload 脚本附加任意属性:

```js title='preload.js' @ts-nocheck // preload with contextIsolation disabled window.myAPI = { doAThing: () => {} }

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

```js title='renderer.js' @ts-nocheck
// use the exposed API in the renderer
window.myAPI.doAThing()

之后:上下文隔离已启用

Electron 中有一个专用模块可以帮助你以无痛的方式完成此操作。contextBridge 模块可用于安全地将 API 从你的 preload 脚本的隔离上下文暴露到网站运行的上下文中。与之前一样,网站也可以在 window.myAPI 上访问该 API。

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

contextBridge.exposeInMainWorld('myAPI', {
  doAThing: () => {}
})

```js title='renderer.js' @ts-nocheck // use the exposed API in the renderer window.myAPI.doAThing()

请阅读上面链接的 `contextBridge` 文档,以完全了解其限制。例如,你无法通过桥接发送自定义原型或符号。

## 安全注意事项 {#security-considerations}

仅仅启用 `contextIsolation` 并使用 `contextBridge` 并不意味着你做的所有事情都是安全的。例如,以下代码是**不安全的**。

```js title='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 接口:

```ts title='interface.d.ts' @ts-noisolate export interface IElectronAPI { loadPreferences: () => Promise, }

declare global { interface Window { electronAPI: IElectronAPI } }

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

```ts title='renderer.ts'
window.electronAPI.loadPreferences()

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 el/electron