To provide suitable levels of security when loading remote content, it is stated that a BrowserWindow
's contextIsolation
and nodeIntegration
options must be enabled and disabled respectively. In this scenario, Node/Electron APIs will not be available to the main renderer process. In order to expose specific functionality, the window's preload script may exploit Electron's contextBridge
feature, providing the main renderer with access to selected Node/Electron APIs.
Despite information provided in the Electron docs, concrete examples of contextBridge
usage are lacking overall. In general, existing documentation/tutorials do not focus on adopting secure practices when implementing an Electron app.
The following is a single contextBridge
usage example I've managed to find online: https://github.com/reZach/secure-electron-template
Would you be able to provide additional resources/examples which might be useful for the implementation of a secure Electron app (which relies on the contextBridge
functionality)?
Insight regarding contextBridge
best practices is also highly appreciated.
Create a safe, bi-directional, synchronous bridge across isolated contexts. Process: Renderer. An example of exposing an API to a renderer from an isolated preload script is given below: // Preload (Isolated World)
Context Isolation is a feature that ensures that both your preload scripts and Electron's internal logic run in a separate context to the website you load in a webContents .
Summary A preload script contains code that runs before your web page is loaded into the browser window. It has access to both DOM APIs and Node. js environment, and is often used to expose privileged APIs to the renderer via the contextBridge API.
Here are all
the steps to set this up
index.js
file, point to the preload script in your BrowserWindow
config object:new BrowserWindow({
webPreferences: {
preload: path.resolve(app.getAppPath(), 'preload.js')
}
})
preload.js
needs to be in your app folder. I use webpack to compile everything, so the preload.js
file is not where my js code is, it's in the app directory where electron-builder
will compile the executable. Here's what the preload.js
contains:process.once('loaded', () => {
const { contextBridge, ipcRenderer, shell } = require('electron')
contextBridge.exposeInMainWorld('electron', {
on (eventName, callback) {
ipcRenderer.on(eventName, callback)
},
async invoke (eventName, ...params) {
return await ipcRenderer.invoke(eventName, ...params)
},
async shellOpenExternal (url) {
await shell.openExternal(url)
},
async shellOpenPath (file) {
await shell.openPath(file)
},
async shellTrashItem (file) {
await shell.trashItem(file)
}
})
})
window.electron.on('nodeJSEvent', (event, param1, param2) => {
console.log('nodeJSEvent has been called with params', param1, param2)
})
const foo = await window.electron.invoke('nodeJSEvent', param1, param2)
console.log(foo)
await window.electron.shellOpenExternal(url)
await window.electron.shellOpenPath(file)
await window.electron.shellTrashItem(file)
That's it. All this code is about giving the possibility to the client side code to call nodejs code that we define in the preload script.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With