Context Isolation definition and purpose
Context Isolation ensures that preload scripts and Electron's internal logic run in a separate context from the website loaded in webContents. This is important for security as it prevents the website from accessing Electron internals or powerful APIs that the preload script has access to.
Context Isolation window object isolation
When Context Isolation is enabled, the window object that the preload script has access to is a different object than the website would have access to. For example, if you set window.hello = 'wave' in your preload script with context isolation enabled, window.hello will be undefined if the website tries to access it.
Context Isolation enabled by default since Electron 12
Context isolation has been enabled by default since Electron 12 and is a recommended security setting for all applications.
Difference between Main World and Isolated World in Electron
The Main World is the context where the website runs. The Isolated World is the separate context where preload scripts and Electron's internal logic run. With Context Isolation enabled, these are separate global contexts, so properties set in one are not accessible in the other.
Renderer process no direct Node.js access
The renderer has no direct access to require or other Node.js APIs. In order to directly include NPM modules in the renderer, you must use the same bundler toolchains (for example, webpack or parcel) that you use on the web.
Node.js integration disabled in renderer for security
Renderer processes can be spawned with a full Node.js environment for ease of development. Historically, this used to be the default, but this feature was disabled for security reasons.
Preload scripts execution and privileges
Preload scripts contain code that executes in a renderer process before its web content begins loading. These scripts run within the renderer context, but are granted more privileges by having access to Node.js APIs.
Preload script attachment in BrowserWindow
A preload script can be attached to the main process in the BrowserWindow constructor's webPreferences option.
const { BrowserWindow } = require('electron')
// ...
const win = new BrowserWindow({
webPreferences: {
preload: 'path/to/preload.js'
}
})
Preload script Window interface access
Because the preload script shares a global Window interface with the renderers and can access Node.js APIs, it serves to enhance your renderer by exposing arbitrary APIs in the window global that your web contents can then consume.
Do not enable Node.js integration for remote content
Node.js integration must not be enabled in any renderer (BrowserWindow, WebContentsView, or <webview>) that loads remote content. Disabling Node.js integration helps prevent XSS attacks from being escalated into Remote Code Execution (RCE) attacks. The default in Electron since 5.0.0 is to have Node.js integration disabled.
Context isolation is required for strong security isolation
Context isolation must be enabled to truly enforce strong isolation and prevent the use of Node primitives, even when nodeIntegration is set to false. Context isolation allows developers to run code in preload scripts and in Electron APIs in a dedicated JavaScript context, preventing global objects like Array.prototype.push or JSON.parse from being modified by scripts running in the renderer process. Context isolation has been the default behavior in Electron since 12.0.0.
Disabling context isolation disables process sandboxing
Disabling context isolation for a renderer process by setting nodeIntegration to true also disables process sandboxing for that process.
Process sandboxing should be enabled
Process sandboxing is a Chromium feature that uses the operating system to significantly limit what renderer processes have access to. Sandboxing should be enabled in all renderers. Loading, reading or processing any untrusted content in an unsandboxed process, including the main process, is not advised. Process sandboxing has been the default behavior in Electron since 20.0.0.
Use HTTPS for remote content
Any resources not included with your application should be loaded using a secure protocol like HTTPS, not HTTP. Similarly, use WSS over WS, FTPS over FTP, and so on. HTTPS ensures data integrity and encrypts traffic between the application and the host.
Define a Content Security Policy
Define a Content Security Policy (CSP) as an additional layer of protection against cross-site-scripting attacks and data injection attacks. CSP can be delivered via an HTTP header using session.defaultSession.webRequest.onHeadersReceived() or via a <meta> tag in HTML. CSP allows the server to restrict and control the resources that can be loaded.
Do not disable webSecurity
Do not disable the webSecurity property on a renderer process (BrowserWindow, WebContentsView, or <webview>). Disabling webSecurity will disable the same-origin policy and set allowRunningInsecureContent to true, allowing execution of insecure code from different domains. This is the default behavior in Electron.
Do not enable allowRunningInsecureContent
Do not set allowRunningInsecureContent to true. This property allows websites loaded over HTTPS to load and execute scripts, CSS, or plugins from insecure sources (HTTP), known as mixed content. This is the default behavior in Electron.
Do not enable experimentalFeatures
Do not enable experimental Chromium features using the experimentalFeatures property. Experimental features have not been enabled for all Chromium users and their impact on Electron has likely not been tested. This is the default behavior in Electron.
Do not use enableBlinkFeatures
Do not use the enableBlinkFeatures property to enable disabled-by-default Blink rendering engine features. There are likely good reasons if a feature was not enabled by default. Under no circumstances should you enable features speculatively. This is the default behavior in Electron.
Do not use allowpopups for webview tags
Do not use the allowpopups attribute on <webview> tags. The allowpopups attribute enables pages and scripts loaded in the webview to create new BrowserWindows using window.open(). Without allowpopups, webview tags are not allowed to create new windows. Follow the principle of minimally required access. This is the default behavior in Electron.
Verify webView options before creation
Control the creation of new <webview> tags from the main process and verify that their webPreferences do not disable security features. Use the 'will-attach-webview' event on the hosting webContents to prevent the creation of webViews with insecure options. Strip away preload scripts if unused, disable Node.js integration, and verify the URL being loaded.
Limit or disable creation of new windows
If you have a known set of windows, limit the creation of additional windows in your app. Use the setWindowOpenHandler() method on webContents to monitor and deny unexpected window creation. This is a common attack vector where attackers try to convince your app to create new windows with more privileges.
Do not use shell.openExternal with untrusted content
Do not use the shell.openExternal API with untrusted content. When openExternal is used with untrusted content, it can be leveraged to execute arbitrary commands. Only use openExternal with URLs you control or have verified.
Use current version of Electron
Strive for always using the latest available version of Electron. Whenever a new major version is released, attempt to update your app as quickly as possible. An application built with an older version of Electron, Chromium, and Node.js is an easier target than one using more recent versions. Security issues and exploits for older versions are more widely available.
Check and configure Electron fuses
Electron ships with a number of options that can be turned off or on using Fuses. Some fuses like runAsNode and nodeCliInspect allow the application to behave differently when run from the command line using specific environment variables or CLI arguments. These can potentially be used to execute commands on the device. Use @electron/fuses module to flip fuses easily.
Security is responsibility of framework, dependencies, and your code
The security of your Electron application is the result of the overall security of the framework foundation (Chromium, Node.js), Electron itself, all NPM dependencies and your code. Keep your application up-to-date with the latest Electron framework release, evaluate your dependencies for vulnerabilities, and adopt secure coding practices.
Displaying arbitrary content from untrusted sources is a severe security risk
Displaying arbitrary content from untrusted sources poses a severe security risk that Electron is not intended to handle. If your application executes code from an online source, it is your responsibility to ensure that the code is not malicious.
Use only local files for Node.js code execution
Under no circumstances should you load and execute remote code with Node.js integration enabled. Instead, use only local files packaged together with your application to execute Node.js code. To display remote content, use the <webview> tag or WebContentsView and make sure to disable nodeIntegration and enable contextIsolation.
Electron security warnings can be controlled via environment variables
Security warnings and recommendations are printed to the developer console, but only show up when the binary's name is Electron. You can force-enable or force-disable these warnings by setting ELECTRON_ENABLE_SECURITY_WARNINGS or ELECTRON_DISABLE_SECURITY_WARNINGS on either process.env or the window object.