Electron apps put a full Chromium browser and a Node.js runtime with operating system access inside one installed program, so a single cross-site scripting bug, unsafe IPC handler or unsigned update can become code execution on every user's machine. Electron hosts widely deployed applications; MITRE ATT&CK names Signal, Slack and Microsoft Teams as examples, and lists abuse of Electron applications as its own technique (T1218.015). This guide covers the Electron process and trust model, the renderer hardening settings that matter, IPC design, navigation and external links, fuses and ASAR integrity, code signing and auto-update, local secrets storage, how enterprises should govern third-party Electron apps, detection and a phased rollout. It is written for desktop application developers, application security engineers and the endpoint teams who have to approve and manage these apps. A short comparison with Tauri covers teams choosing a framework.
Key takeaways
- Treat the renderer as hostile. Keep Electron's secure defaults (context isolation, sandboxing and no Node.js integration) and never turn them off for windows that render remote or user-supplied content.
- IPC is the real attack surface. Expose one narrow function per channel through
contextBridge, validatesenderFrameon every message and never handipcRendererto page code. - Lock the binary at package time. Flip fuses such as
runAsNodeandnodeCliInspectoff and enable ASAR integrity withonlyLoadAppFromAsarbefore signing, or attackers can repurpose your signed app. - Signing keys and update feeds are crown jewels. A stolen signing certificate or update channel turns your distribution into theirs; keep keys in hardware or cloud signing and protect the CI identity that uses them.
- You inherit Chromium's patch cycle. Electron supports only the latest three stable majors and cannot push updates for you; an app that ships an old Electron ships old browser vulnerabilities.
The Electron trust model
An Electron app has a main process (Node.js, full operating system access, creates windows) and one or more renderer processes (Chromium, displaying HTML and JavaScript). A preload script runs before page code in the renderer and is the only intended bridge between the two. Security depends on keeping page code from reaching Node.js.
| Setting | Secure value | Default | What breaks if it is wrong |
|---|---|---|---|
nodeIntegration | false | False since Electron 5.0.0 | Any XSS in the renderer can call Node.js APIs; setting it to true also disables the sandbox |
contextIsolation | true | True since Electron 12.0.0 | Page scripts share a JavaScript context with the preload and Electron APIs; disabling it also disables sandboxing |
sandbox | true (or app.enableSandbox() before ready) | True since Electron 20.0.0 | Renderer loses operating system level process restrictions |
webSecurity | true | True | Same-origin policy is turned off |
allowRunningInsecureContent | false | False | HTTPS pages can load HTTP scripts and CSS |
experimentalFeatures, enableBlinkFeatures | Unset | Off | Unfinished or disabled Chromium features become reachable |
<webview> allowpopups | Absent | Off | Embedded content can open new windows |
Two points from Electron's own documentation are often missed. First, Electron disables some Chrome protections, including Safe Browsing and Certificate Transparency, and calls rendering untrusted content "somewhat uncharted territory". Second, Electron approves all web permission requests (camera, microphone, notifications, geolocation) by default unless you set session.setPermissionRequestHandler(). The safest design loads only the app's own packaged content in privileged windows and opens anything else in the user's default browser.
Renderer hardening
- Load only secure content: use HTTPS, WSS or FTPS for anything external. Prefer bundling UI assets with the app.
- Avoid
file://: Electron's checklist notes thatfile://pages can read any file on the user's machine. Serve app files through a custom scheme withprotocol.handlethat only returns known paths, and disable thegrantFileProtocolExtraPrivilegesfuse if you do not serve fromfile://. - Set a Content Security Policy: start from
script-src 'self', avoid wildcards, set it throughwebRequest.onHeadersReceivedor a<meta>tag for local files, and addobject-src 'none'. See the Security Headers Guide. - Escape and sanitise rendered data: chat messages, file names, Markdown and notification text are the usual XSS sources in desktop apps. Frameworks such as React help; their escape hatches do not. See the React and Next.js Security Guide and the Cross-Site Scripting (XSS) Prevention Guide.
- Handle permission requests: install a handler in every session that loads remote content and approve only named permissions for named origins.
- Restrict
<webview>: handlewill-attach-webviewonweb-contents-created, strip anypreload, forcenodeIntegrationoff and checkparams.srcagainst an allowlist.
IPC and preload design
Context isolation only helps if the bridge is narrow. Electron's context isolation tutorial gives the unsafe pattern explicitly: exposing ipcRenderer.send to the page lets any website loaded in that window send arbitrary IPC messages. The safe pattern exposes one purpose-built function per message.
- One function per channel: expose functions such as
loadPreferences()throughcontextBridge.exposeInMainWorld, each callingipcRenderer.invokeon one fixed channel with typed arguments. - Never expose raw callbacks: the checklist warns against exposing
ipcRenderer.ondirectly because the event object exposessender; wrap callbacks and pass only the value needed. - Validate the sender on every handler: check
event.senderFrame.originagainst an allowlist in the main process, deny if the frame is null, and do not trust URLs such asabout:blankorblob:that do not identify their controller. - Validate arguments in the main process: treat every IPC argument as untrusted input. A handler that accepts a file path, shell command or URL from the renderer is a privilege escalation path unless it checks against a fixed allowlist.
- Least privilege in the main process: do not build a generic "run this" or "read this file" API. Each privileged capability should exist only because a feature needs it, and be reviewed as such.
| Threat | Electron control | ATT&CK technique |
|---|---|---|
| XSS in renderer reaches Node.js | nodeIntegration: false, contextIsolation: true, sandbox, CSP | T1059.007 JavaScript, T1203 Exploitation for Client Execution |
| Renderer abuses a broad IPC handler | Per-channel functions, senderFrame validation, argument allowlists | T1203 |
| Malicious link opens a dangerous protocol handler | shell.openExternal only with validated https: URLs | T1204.002 Malicious File |
| Signed app launched as a generic Node.js runtime or debugged | Fuses: runAsNode, nodeOptions, nodeCliInspect off | T1218.015 Electron Applications |
| App JavaScript modified on disk for persistence | ASAR integrity plus onlyLoadAppFromAsar, code signing | T1554 Compromise Host Software Binary |
| Malware signed with a stolen certificate | Hardware-backed or cloud signing, CI identity protection, revocation plan | T1553.002 Code Signing |
| Local tokens read by infostealers | safeStorage, cookieEncryption fuse, short-lived tokens | T1555 Credentials from Password Stores, T1552.001 Credentials In Files |
| Malicious plugin or extension loaded by the app | Signed, allowlisted extensions; no runtime code download | T1176 Software Extensions |
Navigation, new windows and external links
- Block unexpected navigation: handle
will-navigateand callpreventDefault()for anything not on the allowlist. Parse with Node'sURLclass and compare origins exactly; Electron warns that astartsWithcheck can be bypassed with a host such asexample.com.attacker.com. - Deny new windows by default: use
setWindowOpenHandlerand return{ action: 'deny' }unless the URL is expected. Where a link should open, hand it to the system browser. - Treat
shell.openExternalas a command: Electron's checklist says untrusted input here can lead to arbitrary command execution, because the operating system dispatches the URL to whatever handles its scheme. Allow onlyhttps:(and perhapsmailto:) after parsing.
Fuses, ASAR integrity and the packaged binary
Fuses are feature toggles stored in the Electron binary and flipped at package time, before code signing, so that the operating system's signature check stops them being flipped back. Several defaults favour developer convenience over security.
| Fuse | Default | Recommended for production | Why |
|---|---|---|---|
runAsNode | Enabled | Disable | Stops ELECTRON_RUN_AS_NODE turning your signed binary into a general Node.js runtime (use Utility Processes instead of child_process.fork) |
nodeOptions | Enabled | Disable | Ignores NODE_OPTIONS and NODE_EXTRA_CA_CERTS from the environment |
nodeCliInspect | Enabled | Disable | Ignores --inspect and --inspect-brk, and stops SIGUSR1 starting the inspector |
embeddedAsarIntegrityValidation | Disabled | Enable (macOS, Windows) | Validates app.asar against a hash stored outside the archive |
onlyLoadAppFromAsar | Disabled | Enable | Without it, Electron notes that integrity checking can be bypassed through the code search path |
cookieEncryption | Disabled | Enable for new apps | Encrypts the cookie store with operating system keys; one-way, and requires signing on macOS |
grantFileProtocolExtraPrivileges | Enabled | Disable if not using file:// | Removes extra privileges for file:// pages |
loadBrowserProcessSpecificV8Snapshot | Disabled | Consider | Separate main process snapshot; some startup cost |
Flip fuses with @electron/fuses or the Electron Forge fuses plugin, and verify the shipped build in CI with npx @electron/fuses read --app <path>. ASAR integrity is supported on macOS from Electron 16.0.0 and Windows from 30.0.0; the header hash lives in an ElectronAsarIntegrity entry in Info.plist on macOS and an ElectronAsar integrity resource on Windows. Electron Forge 7.4.0 and Electron Packager 18.3.1 or later set this up automatically. Electron still labels the feature experimental and it is not documented for Linux, so Linux builds rely on package manager signatures and file permissions.
Code signing and auto-update
Signing is both a distribution requirement and a security dependency. Electron's documentation lists safeStorage, the cookieEncryption fuse and autoUpdater on macOS as relying on a signed app.
- macOS: sign and notarise with Apple; Electron Forge uses
@electron/osx-signand@electron/notarize. Keep Apple ID and API credentials for notarisation in the CI secrets store, not in build config. - Windows: sign with
@electron/windows-signor your packager's signer. Electron's documentation notes that code signing keys must be held on FIPS 140 Level 2 or Common Criteria EAL 4+ hardware, so they cannot simply be copied to CI; cloud signing services such as Azure Artifact Signing (formerly Azure Trusted Signing) or HSM-backed key services are the usual answer. - Protect the signing identity: the signing pipeline is a non-human identity with the power to make malware look like your product. Restrict which pipelines and branches can request a signature, log every signing event and review it. See the Code Signing Security Guide.
- Update channel: Electron's
autoUpdateruses Squirrel.Mac and Squirrel.Windows; alternatives are update.electronjs.org (public GitHub repositories with releases on GitHub Releases), static storage feeds and self-hosted servers. Serve feeds over HTTPS only, verify signatures of downloaded updates, restrict who can publish releases and run update logic only whenapp.isPackagedis true. - Plan revocation: know which versions would stop working if you revoke a certificate and how users will be moved to a re-signed build.
The GitHub code signing certificate theft in December 2022 shows the stakes. A compromised personal access token for a machine account was used to clone repositories for Atom, GitHub Desktop and other deprecated projects, which contained encrypted Windows and Apple Developer ID signing certificates. GitHub revoked the certificates by 2 February 2023, which broke GitHub Desktop for Mac versions 3.0.2 to 3.1.2 and Atom 1.63.0 and 1.63.1 until users updated or downgraded. Signing material in a repository, guarded by one long-lived token, is the failure pattern to avoid.
Local secrets and session data
Desktop apps keep OAuth refresh tokens, session cookies and API keys on disk, which makes them a target for infostealers.
- Use
safeStorage: it encrypts with Keychain on macOS, DPAPI on Windows and kwallet or libsecret on Linux. Electron's documentation is candid about limits: DPAPI protects against other users but not other apps running as the same user, and on Linux without a secret store thebasic_textbackend uses a hard-coded password and is effectively unprotected. CheckgetSelectedStorageBackend()and refuse to store long-lived secrets onbasic_text. - Move to the async API: the synchronous
encryptStringanddecryptStringare deprecated and scheduled for removal in Electron 46;encryptStringAsyncanddecryptStringAsyncsupport key rotation throughshouldReEncrypt. - Prefer short-lived tokens: local encryption slows theft but does not stop malware running as the user. Short access token lifetimes, refresh token rotation and server-side revocation limit what a stolen token is worth. See the Token and Session Security Guide.
- Never ship backend secrets in the app: anything in
app.asarcan be extracted by any user. API keys that must not be public belong behind your own backend. See the Secrets Management Guide.
Keeping Electron current
Electron releases a new major version every eight weeks, tracks even-numbered Chromium releases and supports only the latest three stable majors, with only the latest minor in each line receiving fixes. Because each app bundles its own Chromium and Node.js, Chromium vulnerabilities, including those in CISA's KEV catalogue, stay exploitable in your app until you ship a new Electron. Electron's sandbox documentation states that it cannot push security updates directly, so the app must upgrade itself.
- Set an Electron patch SLA tied to Chromium security releases, and track the Electron version as a component in your SBOM. See the SBOM Guide.
Choosing a framework: Electron and Tauri
| Aspect | Electron | Tauri 2 |
|---|---|---|
| Web engine | Bundled Chromium per app | Operating system WebView |
| Privileged backend | Node.js main process | Rust core and plugins |
| Bridge | Preload with contextBridge and IPC | IPC to commands gated by capabilities, permissions and scopes |
| Patching of web engine | App vendor must ship a new Electron | Tauri notes WebView patches can reach users faster through the OS |
| Extra containment | Sandbox, fuses, ASAR integrity | Isolation pattern, CSP |
Neither framework removes the need for a narrow bridge. Tauri's capability model makes the allowlist explicit in configuration; in Electron the same discipline must come from code review. Choose Electron where consistent rendering and a large JavaScript codebase matter; Tauri suits new projects that can accept platform WebView differences and want an explicit command allowlist.
Managing third-party Electron apps in the enterprise
Most organisations run more Electron apps than they build: chat, meeting, password manager, note-taking and developer tools. Endpoint teams have limited control over the vendor's settings, but several things help.
- Allowlist by publisher signature, not path, using application control such as Windows App Control for Business or macOS Gatekeeper and MDM policy. Per-user installs into writable profile folders are common with Electron apps and deserve attention. See the Application Allowlisting Guide.
- Track bundled Electron versions in software inventory and push vendors that lag beyond the supported window.
- Ask vendors security questions during procurement: fuse state, sandbox and context isolation settings, signing and update process, and how they store tokens.
- Govern plugins and extensions: editors and chat clients built on Electron load third-party extensions with user rights. The GlassWorm campaign used malicious editor extensions on Open VSX and Microsoft's marketplace to steal npm, GitHub and Open VSX credentials from developers. See the Browser Extension Security Guide.
Detection and measurement
- Process lineage: alert when an Electron app's process spawns shells, script interpreters or LOLBins, or is launched with
--inspect,--remote-debugging-port,--gpu-launcheror withELECTRON_RUN_AS_NODEset. ATT&CK T1218.015 describes abuse of Electron apps such asteams.exeto run commands as child processes. See the Endpoint Security Guide. - File integrity: monitor writes to
resources/app.asarand the application folder outside of signed updates (T1554). - Signature status: report Electron executables whose signature is missing, invalid or from an unexpected publisher.
- App telemetry: log denied navigations, denied window opens, rejected IPC senders and permission denials; a spike suggests injected content probing the bridge.
Metrics:
- Days between a Chromium security release and your app shipping the corresponding Electron release.
- Percentage of releases whose CI fuse check confirms
runAsNode,nodeOptionsandnodeCliInspectare disabled and ASAR integrity is enabled. - Number of windows with
sandbox,contextIsolationorwebSecurityweakened (target zero). - Count of IPC handlers without sender validation, from static analysis.
- Percentage of managed endpoints running third-party Electron apps on a supported Electron major.
Rolling out the controls
- Baseline: scan the codebase for
webPreferencessettings,ipcMainhandlers,shell.openExternalcalls andfile://loads, and runnpx @electron/fuses readon the current build. Record the Electron version against the support window. - Restore secure defaults: remove any
nodeIntegration: true,contextIsolation: falseorsandbox: false; move needed Node.js work into the main process behind narrow IPC. Test each window in a pilot build with internal users. - Refactor IPC: replace generic channels with per-feature functions, add
senderFramechecks and argument validation, and add unit tests that call handlers from an unexpected origin. - Add navigation and permission handlers and a CSP; ship to a beta channel and watch denial logs before general release.
- Flip fuses and enable ASAR integrity in the packaging step, add the CI fuse check, and confirm signing still validates. Roll back by shipping the previous signed build through the update channel; fuses cannot be changed in the field.
- Harden signing and updates: move keys to HSM or cloud signing, restrict the signing pipeline, require HTTPS feeds and document a revocation runbook.
- Migrate secrets to async
safeStorageand enablecookieEncryptionfor new installs, remembering that it is one-way.
Small teams should use Electron Forge templates (which include the fuses plugin), a cloud signing service and update.electronjs.org or a static feed, and spend review time on IPC. Large organisations should publish an internal Electron baseline (settings, fuses, signing, update and logging requirements), enforce it with CI checks across all desktop products, and use the same baseline as procurement questions for third-party apps.
Common failure patterns
- Re-enabling Node.js in the renderer to make a feature work quickly, which turns the next XSS bug into remote code execution.
- A generic IPC bridge such as exposing
ipcRenderer.sendor an "exec" handler, which context isolation cannot save. - Passing chat or document links straight to
shell.openExternalwithout scheme validation. - Shipping default fuses, leaving
runAsNodeand the inspector available so attackers can run their own code under your signature. - Signing secrets in the repository or on CI disks, guarded by a single long-lived token, as in the GitHub Desktop and Atom certificate theft.
- Falling outside Electron's support window, so publicly known Chromium flaws remain exploitable in the shipped app.
Framework mapping
| Control area | NIST SP 800-53 Rev. 5 | NIST CSF 2.0 | MITRE D3FEND / ATT&CK |
|---|---|---|---|
| Secure renderer defaults and narrow IPC | SA-8 Security and Privacy Engineering Principles, CM-7 Least Functionality, SC-18 Mobile Code | PR.PS-06 | T1059.007, T1203 |
| Fuses, ASAR integrity and code signing | SI-7 Software, Firmware, and Information Integrity | PR.PS-02 | T1554, T1553.002, T1218.015 |
| Electron and dependency patching | SI-2 Flaw Remediation, SR-3 Supply Chain Controls and Processes | ID.RA-01, PR.PS-02 | D3-SU Software Update |
| Local secret storage | SC-28 Protection of Information at Rest | PR.DS-01 | T1555, T1552.001 |
| Enterprise allowlisting of desktop apps | CM-7 | PR.PS-05 | D3-EAL Executable Allowlisting |
| Endpoint monitoring of Electron processes | SI-4 System Monitoring | DE.CM-09 | T1218.015 |
In ISO/IEC 27001:2022 Annex A the work falls mainly under A.8.25 (secure development life cycle), A.8.28 (secure coding) and A.8.19 (installation of software on operational systems); in CIS Controls v8.1, Control 16 (Application Software Security) for builders and Control 2 (Inventory and Control of Software Assets) for enterprises. See the Application Security Guide.
Practitioner checklist
- Confirm every window runs with
nodeIntegration: false,contextIsolation: true,sandbox: trueandwebSecurity: true, and callapp.enableSandbox()beforeready. - Expose only per-feature functions through
contextBridgeand validatesenderFrameand arguments in everyipcMainhandler. - Handle
will-navigate,setWindowOpenHandlerand permission requests with default-deny allowlists. - Validate every URL passed to
shell.openExternaland every deep link the app receives. - Serve app content through
protocol.handlerather thanfile://, with a CSP starting fromscript-src 'self'. - Disable
runAsNode,nodeOptionsandnodeCliInspect, enable ASAR integrity withonlyLoadAppFromAsar, and verify fuses in CI before signing. - Keep signing keys in HSM or cloud signing, restrict the signing pipeline identity and maintain a revocation runbook.
- Store tokens with async
safeStorage, refusebasic_texton Linux and keep backend secrets out of the app bundle. - Stay within Electron's three supported majors with a patch SLA tied to Chromium security releases.
- For third-party Electron apps, allowlist by publisher, inventory Electron versions and govern extensions.
Standards and references
- Electron: Security checklist
- Electron: Context Isolation
- Electron: Electron Fuses
- Electron: ASAR Integrity
- Electron: Code Signing
- Electron: safeStorage
- MITRE ATT&CK: System Binary Proxy Execution: Electron Applications
- Tauri: Security
Related NHI Mgmt Group resources: Application Security Guide · Code Signing Security Guide · Endpoint Security Guide · React and Next.js Security Guide · OWASP Top 10 Guide