Avoiding Pitfalls Developing with Electron
Note: A previous version of this blog post recommended relying on the Same Origin policy as a security barrier. Since publication, new Same Origin policy bybasses have been presented by Luca Carettoni (https://www.blackhat.com/us-17/briefings.html#electronegativity-a-study-of-electron-security). We have therefore removed the recommendation that this policy be used defensively.
Electron is an increasingly mature and popular framework for cross-platform application development with a low bar to entry for experienced web developers due to the fact it uses the Chromium browser source for its GUI platform and Node.js for its system layer.
As it becomes increasingly popular it is important to look at some of the security pitfalls that can occur using it. At present, Electron provides a number of robust security features, but many of them are not enabled by default.
This blog post attempts to list some of the most significant pitfalls to avoid while developing Electron apps. However, as a relatively new framework there may be a number of other issues which become apparent over time and Electron itself is under significant ongoing development.
The short version
- Use data-binding frameworks like AngularJS, Vue.js or React consistently to render any data in order to reduce the risk of XSS. Never print raw data in the DOM.
- Set nodeIntegration to False whenever you open a window.
- If your Electron app integrates content from a web application, it should only ever access it over HTTPS.
- Intercept dragged link dropping and unexpected location changing events to prevent application flow expectations from being circumvented.
- Validate information from query URLs, even for local APIs.
- Avoid including Node.js modules directly in your window – or risk bypassing your own sandbox.
- Never execute or evaluate the contents of IPC messages.
- Be aware that source for native dependencies that require compilation as part of your build will be disclosed to everyone who downloads your app (as will NPM package metadata, including version numbers).
And the slightly longer version
Don’t render untrusted GUI elements
In most Electron applications, GUI components will be the simplest and most prominent targets for attackers. As with web application development, it is critical to maintain a clear distinction between application data being presented and templating/application code used to present it, in order to prevent XSS.
The main difference when developing with Electron is that XSS may give an attacker full control of the underlying system or, at the very least, ability to interact with local components. This can even be available if your application can be redirected to display an attacker controlled web page in any way, potentially turning links and open redirects into remote code execution vulnerabilities.
The first line of defence here is to avoid ever composing DOM elements from dynamic content directly. Instead, always render content safely using a templating or data-binding library.
Given the risks associated with rendering untrusted content, however, it is still wise to design applications for security in depth, restricting the execution available to a successful attacker.
Node integration is the feature which embeds core Node.js functionality in window execution contexts. It is on by default, providing developers (and potentially attackers) with core system functionality. While it may be very convenient to access all of this programming power directly, it effectively bypasses the security sandbox.
This feature is enabled or disabled when windows are created, and given the risks, should almost always be disabled (https://github.com/electron/electron/blob/master/docs/api/browser-window.md#class-browserwindow) for windows in production applications.
Instead, if you need selected node-based functionality in a given window, it can be embedded using a preload script, defined in the webPreferences object.
Only use HTTPS for integration with web applications
A consequence of the node integration issue, is that not only is it dangerous to render untrusted content in local GUI elements unsafely, but any successful MITM attack of remote content rendered by an Electron app directly allows the attacker to execute code in the BrowserWindow sandbox, and potentially even break out.
Consequently, even if HTTPS is considered unnecessary for confidentiality reasons, it is critical that remote content is only ever accessed over HTTPS, to protect integrity in transit.
Intercept and safely handle unexpected navigation
It’s possible for application flow in many cases to be controlled by dragging and dropping links on the window. This may become a security issue if local users can bypass control flow (such as, for example, password lock screens) by dropping a link to a screen on top of the window. While it’s often possible for a user with local machine access to read the process memory and extract any sensitive information, windows should be protected by listening for and intercepting dropped link events to prevent undesirable navigation and potentially security bypasses by a local user.
Unexpected navigation may alternatively be the result of social engineering attacks with links sent to users.
Just because it’s local, doesn’t mean it’s safe
It is common for Electron apps to store and use URL query strings and APIs to handle context data. However, while this may be convenient to provide an API to underlying controller functionality, these query strings shouldn’t be any more trusted for functionality than API parameters in web development. There are a number of ways an attacker might redirect an Electron window or socially engineer a user to redirect it themselves.
One publically distributed Electron application was discovered to pass a URL query string containing parameters for configuring a new window, which on opening was sent to a handler which applied those parameters to a window constructor. An attacker who was able to redirect this window (whether through an open redirect, XSS, or malicious links embedded in messages or some other mechanism) might, for example, redirect to an API query URL with reconfigured parameters to open a window with a malicious preload script, disabled security settings or a number of other features of interest to an attacker.
All URL-based APIs should validate queries against expectations before any further action and rejected if they fail, just as is the norm in in web application development.
Preload script peril
Beware of using preload scripts to include root Node.js modules directly in windows – some of them provide routes for a sandboxed attacker to regain full Node.js functionality.
For example, the process module in contains a reference to Node.js’ require() function from which a sandboxed attacker can import any other node functionality and break out of the sandbox. Direct fs module access may allow an attacker to overwrite system files with backdoored versions. A safer approach is to use the preload script to embed custom wrapper functions to system functionality limiting the impact of XSS.
In general, the content of preload scripts should be scrutinised closely.
IPC messaging security
When using IPC messages to communicate between processes, it’s important not to yield uncontrolled execution ability from one process to another. A significant benefit of Electron’s architecture is the ability to easily segment processes and provide APIs with restricted permissions to potentially vulnerable UI components. Messages are serialized intermediately as JSON objects which prevents transmission of functions or function calls, but it’s important that IPC messages are not trusted, and are handled safely (i.e.: not evaluated or executed, or used directly to control execution without validation), or this may provide an attacker a route to bypass sandbox security.
The archive format for Electron is readily unpackable using open source tools and the bar to reverse engineering them is often trivial. This isn’t in itself a security issue, but there are a wide variety of publicly available Electron based applications which are based on a mixture of dependencies from both open and private repositories.
At present there don’t appear to be any good ways of preventing this source disclosure besides pre-compiling dependency packages as node modules ahead of compiling/packaging the Electron application for distribution. This itself can pose headaches maintaining cross-compatibility.
Of course, this also means any out-of-date dependencies with publicly-known vulnerabilities are also clearly visible to anyone inspecting your application.
Electron provides many powerful features and a relatively accessible entry point to GUI development, especially for developers from a web development background. However, it’s important to design applications conscious of the risks associated with both the web development and desktop application development technologies in use.
Written by Phoebe Queen
First published on 16/09/16