In Electron, check the sender of every privileged IPC message in the main process. Look at the frame that sent it and compare its origin to what you expect. Do not rely on the fact that your own window made the call, because frames inside that window may not be yours.
Why the sender matters
The main process holds the powerful APIs: files, shell, native menus, credentials. Renderers ask it to act through IPC. If a handler trusts any caller, then any frame that can reach the channel can trigger the action.
A window may contain more than your bundled page. An iframe, an embedded widget, a page the user navigated to, or injected content can all send messages if a bridge exposes the channel. The security guide lists sender validation among its recommendations for this reason.
Check the origin, not the URL string
With ipcMain.handle, the event carries senderFrame. The guide says to check the origin, not the full URL. Parse it with a URL parser. String prefix checks are easy to bypass, for instance with a host name that begins with your host name.
ipcMain.handle('read-config', (event) => {
const origin = new URL(event.senderFrame.url).origin
if (origin !== 'app://main') {
throw new Error('Untrusted sender')
}
return loadConfig()
})
The app://main value above is an example for a custom protocol. Use whatever your app actually serves.
Combine it with other limits
Sender validation is one layer.
- Serve the app from a custom protocol instead of
file://, so the origin is meaningful. The guide notes thatfile://pages can read any file on the machine. - Block navigation and new windows so the window cannot end up on a foreign origin.
- Keep handlers narrow: one operation, validated arguments.
- Return only what the caller needs.
Testing it
Write a test that sends the same message from a frame with a different origin and expects a refusal. If you cannot easily create such a frame, that is a hint that you have not thought about the case.
When your app does load remote content
Some apps must show a remote page. Give that window no preload bridge to privileged channels, or give it a separate, smaller bridge. Do not reuse the same preload for local and remote content.
Quick check
Is it enough to hide the channel name from the page?
No. A hidden name is not access control. Frames can discover or be handed names, and your handler cannot tell who called unless it checks the sender.
Should read only handlers be validated too?
Yes. A read can expose tokens, paths or user data. Apply the same check, then decide what each caller may see.
Sources
Last reviewed: 19 October 2026.