Security defaults
An Electron app runs with the user's permissions: it can read and delete their files. If a foreign script gets into a window — from a note copied from the internet, or from a page opened through a link — it gets the same permissions. Security settings restrict the page so that such a script can do nothing.
Three window settings
All three are on by default in current Electron versions. Written out explicitly they look like this:
// main.js
const win = new BrowserWindow({
width: 900,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
sandbox: true,
},
});You do not have to write them; the behaviour is the same. Writing them out is a useful reminder for whoever reads the code.
| Setting | Default | What it does |
|---|---|---|
nodeIntegration | false | The page cannot call require or use Node.js |
contextIsolation | true | Preload and the page run in separate JavaScript worlds |
sandbox | true | The operating system restricts the renderer, like a Chrome tab |
Context isolation
With isolation, preload and the page have different window objects. The page cannot replace a function that preload uses and cannot reach ipcRenderer. It sees only what was put there with contextBridge.exposeInMainWorld.
Content-Security-Policy
CSP is a rule about where the page may load scripts, styles and images from. It is set with a meta tag in index.html:
<!-- index.html -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; img-src 'self' data:">'self' allows only files from the app folder, and img-src additionally allows data: images. Inline scripts and foreign sites are blocked: even if a script tag gets into a note, it does not run.
A narrow bridge
Pass individual functions with a clear purpose through contextBridge:
// preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('notes', {
save: (list) => ipcRenderer.invoke('notes:save', list),
});The page can only save notes. If preload exposed the whole ipcRenderer, the page could send any message to the main process.
Other rules
- Output user text with
textContent, notinnerHTML. - Do not open foreign sites in a window that has your preload. Open external links in the browser with
shell.openExternal(url)in the main process. - Keep Electron updated: new versions fix Chromium vulnerabilities.
Common mistake
When they see require is not defined, beginners set nodeIntegration: true and contextIsolation: false, as old articles suggest. After that any script on the page gets full access to the computer. Keep the defaults, move Node.js work into the main process and give the page a function through preload.