Safeguard
Developer Tools•

Electron contextBridge: expose narrow functions, not raw IPC

How to design a safe Electron preload: one purpose built function per IPC message, no pass through of ipcRenderer, and a pattern you can review in minutes.

Safeguard Security Team
3 min read

In an Electron preload script, expose one small function per operation, such as loadPreferences, and never hand the page ipcRenderer.send or a similar generic call. Context isolation only creates the boundary. What you place on the bridge decides whether that boundary protects anything.

What the bridge does

With context isolation on, your preload runs in a separate JavaScript context from the page. The page cannot read what the preload put on its own window object. To share an API you call contextBridge.exposeInMainWorld, and the page then sees only what you exposed.

That is a good design, and it is easy to undo with one line.

The unsafe pattern

The Electron docs show the mistake plainly. Passing ipcRenderer.send straight through gives any page the ability to pick the channel and the payload. Any website your window loads, or any injected script, can then send arbitrary IPC messages to the main process.

// Unsafe: the page chooses channel and arguments
contextBridge.exposeInMainWorld('myAPI', {
  send: ipcRenderer.send
})

The safe pattern

Expose a fixed operation for each message you intend to support.

// Safer: one fixed operation
contextBridge.exposeInMainWorld('electronAPI', {
  loadPreferences: () => ipcRenderer.invoke('load-prefs')
})

The channel name lives in the preload, not in the page. The page cannot invent new ones.

Do not leak the event object

The security guide also warns about listeners. If you expose a function that registers a callback straight onto ipcRenderer.on, the callback receives the event, including sender details. Wrap the callback so that only the value is passed through.

Validate on the main side as well

The preload is a convenience, not a gate. Treat every message arriving in the main process as untrusted. Check the shape of the arguments, check where the message came from, and keep the handler narrow. The related post on validating IPC senders covers the origin check.

Review checklist

  • Every exposed function maps to one named operation.
  • No function accepts a channel name from the page.
  • No function takes a path, command or URL and passes it on unchecked.
  • Callbacks do not expose the IPC event object.
  • Types are simple values. The bridge cannot carry custom prototypes or symbols anyway.

If your preload has more than a dozen exposed functions, ask whether the renderer really needs all of them. Each one is an entry point into the privileged side of the app.

Quick check

Is it safe to expose a function that reads any file path the page asks for?

No. Even a narrow looking name becomes a file read primitive if the argument is not restricted. Accept an identifier for a known file, and resolve the path on the main side.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.