內容隔離
這是什麼?
內容隔離是一項功能,可確保您的 preload
腳本和 Electron 的內部邏輯都在與您在 webContents
中載入的網站不同的上下文中執行。這對於安全目的非常重要,因為它有助於防止網站存取 Electron 內部或您的預載腳本有權存取的強大 API。
這表示您的預載腳本有權存取的 window
物件實際上與網站有權存取的物件不同。 例如,如果您在預載腳本中設定 window.hello = 'wave'
並且啟用內容隔離,如果網站嘗試存取,window.hello
將會是 undefined。
自 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()