跳到主要內容

內容隔離

這是什麼?

內容隔離是一項功能,可確保您的 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()