diff --git a/docs/tutorial/performance.md b/docs/tutorial/performance.md index 99fefa7a4d..f1648769ef 100644 --- a/docs/tutorial/performance.md +++ b/docs/tutorial/performance.md @@ -1,3 +1,11 @@ +--- +title: Performance +description: A set of guidelines for building performant Electron apps +slug: performance +hide_title: true +toc_max_heading_level: 3 +--- + # Performance Developers frequently ask about strategies to optimize the performance of @@ -49,7 +57,7 @@ at once, consider the [Chrome Tracing](https://www.chromium.org/developers/how-t * [Get Started With Analyzing Runtime Performance][chrome-devtools-tutorial] * [Talk: "Visual Studio Code - The First Second"][vscode-first-second] -## Checklist +## Checklist: Performance recommendations Chances are that your app could be a little leaner, faster, and generally less resource-hungry if you attempt these steps. @@ -62,7 +70,7 @@ resource-hungry if you attempt these steps. 6. [Unnecessary or blocking network requests](#6-unnecessary-or-blocking-network-requests) 7. [Bundle your code](#7-bundle-your-code) -## 1) Carelessly including modules +### 1. Carelessly including modules Before adding a Node.js module to your application, examine said module. How many dependencies does that module include? What kind of resources does @@ -70,7 +78,7 @@ it need to simply be called in a `require()` statement? You might find that the module with the most downloads on the NPM package registry or the most stars on GitHub is not in fact the leanest or smallest one available. -### Why? +#### Why? The reasoning behind this recommendation is best illustrated with a real-world example. During the early days of Electron, reliable detection of network @@ -99,7 +107,7 @@ running Linux might be bad news for your app's performance. In this particular example, the correct solution was to use no module at all, and to instead use connectivity checks included in later versions of Chromium. -### How? +#### How? When considering a module, we recommend that you check: @@ -128,7 +136,7 @@ In this example, on the author's machine, we saw that loading `request` took almost half a second, whereas `node-fetch` took dramatically less memory and less than 50ms. -## 2) Loading and running code too soon +### 2. Loading and running code too soon If you have expensive setup operations, consider deferring those. Inspect all the work being executed right after the application starts. Instead of firing @@ -141,7 +149,7 @@ using the same strategy _and_ are using sizable modules that you do not immediately need, apply the same strategy and defer loading to a more opportune time. -### Why? +#### Why? Loading modules is a surprisingly expensive operation, especially on Windows. When your app starts, it should not make users wait for operations that are @@ -157,14 +165,14 @@ immediately display the file to you without any code highlighting, prioritizing your ability to interact with the text. Once it has done that work, it will move on to code highlighting. -### How? +#### How? Let's consider an example and assume that your application is parsing files in the fictitious `.foo` format. In order to do that, it relies on the equally fictitious `foo-parser` module. In traditional Node.js development, you might write code that eagerly loads dependencies: -```js +```js title='parser.js' const fs = require('fs') const fooParser = require('foo-parser') @@ -187,7 +195,7 @@ In the above example, we're doing a lot of work that's being executed as soon as the file is loaded. Do we need to get parsed files right away? Could we do this work a little later, when `getParsedFiles()` is actually called? -```js +```js title='parser.js' // "fs" is likely already being loaded, so the `require()` call is cheap const fs = require('fs') @@ -223,7 +231,7 @@ module.exports = { parser } In short, allocate resources "just in time" rather than allocating them all when your app starts. -## 3) Blocking the main process +### 3. Blocking the main process Electron's main process (sometimes called "browser process") is special: It is the parent process to all your app's other processes and the primary process @@ -235,7 +243,7 @@ Under no circumstances should you block this process and the UI thread with long-running operations. Blocking the UI thread means that your entire app will freeze until the main process is ready to continue processing. -### Why? +#### Why? The main process and its UI thread are essentially the control tower for major operations inside your app. When the operating system tells your app about a @@ -246,31 +254,31 @@ the GPU process about that – once again going through the main process. Electron and Chromium are careful to put heavy disk I/O and CPU-bound operations onto new threads to avoid blocking the UI thread. You should do the same. -### How? +#### How? Electron's powerful multi-process architecture stands ready to assist you with your long-running tasks, but also includes a small number of performance traps. -1) For long running CPU-heavy tasks, make use of +1. For long running CPU-heavy tasks, make use of [worker threads][worker-threads], consider moving them to the BrowserWindow, or (as a last resort) spawn a dedicated process. -2) Avoid using the synchronous IPC and the `remote` module as much as possible. -While there are legitimate use cases, it is far too easy to unknowingly block -the UI thread using the `remote` module. +2. Avoid using the synchronous IPC and the `@electron/remote` module as much +as possible. While there are legitimate use cases, it is far too easy to +unknowingly block the UI thread. -3) Avoid using blocking I/O operations in the main process. In short, whenever +3. Avoid using blocking I/O operations in the main process. In short, whenever core Node.js modules (like `fs` or `child_process`) offer a synchronous or an asynchronous version, you should prefer the asynchronous and non-blocking variant. -## 4) Blocking the renderer process +### 4. Blocking the renderer process Since Electron ships with a current version of Chrome, you can make use of the latest and greatest features the Web Platform offers to defer or offload heavy operations in a way that keeps your app smooth and responsive. -### Why? +#### Why? Your app probably has a lot of JavaScript to run in the renderer process. The trick is to execute operations as quickly as possible without taking away @@ -280,7 +288,7 @@ at 60fps. Orchestrating the flow of operations in your renderer's code is particularly useful if users complain about your app sometimes "stuttering". -### How? +#### How? Generally speaking, all advice for building performant web apps for modern browsers apply to Electron's renderers, too. The two primary tools at your @@ -300,14 +308,14 @@ some caveats to consider – consult Electron's for any operation that requires a lot of CPU power for an extended period of time. -## 5) Unnecessary polyfills +### 5. Unnecessary polyfills One of Electron's great benefits is that you know exactly which engine will parse your JavaScript, HTML, and CSS. If you're re-purposing code that was written for the web at large, make sure to not polyfill features included in Electron. -### Why? +#### Why? When building a web application for today's Internet, the oldest environments dictate what features you can and cannot use. Even though Electron supports @@ -323,7 +331,7 @@ It is rare for a JavaScript-based polyfill to be faster than the equivalent native feature in Electron. Do not slow down your Electron app by shipping your own version of standard web platform features. -### How? +#### How? Operate under the assumption that polyfills in current versions of Electron are unnecessary. If you have doubts, check [caniuse.com](https://caniuse.com/) @@ -338,12 +346,12 @@ If you're using a transpiler/compiler like TypeScript, examine its configuration and ensure that you're targeting the latest ECMAScript version supported by Electron. -## 6) Unnecessary or blocking network requests +### 6. Unnecessary or blocking network requests Avoid fetching rarely changing resources from the internet if they could easily be bundled with your application. -### Why? +#### Why? Many users of Electron start with an entirely web-based app that they're turning into a desktop application. As web developers, we are used to loading @@ -360,7 +368,7 @@ will take care of the rest. When building an Electron app, your users are better served if you download the fonts and include them in your app's bundle. -### How? +#### How? In an ideal world, your application wouldn't need the network to operate at all. To get there, you must understand what resources your app is downloading @@ -387,21 +395,21 @@ without shipping an application update is a powerful strategy. For advanced control over how resources are being loaded, consider investing in [Service Workers][service-workers]. -## 7) Bundle your code +### 7. Bundle your code As already pointed out in "[Loading and running code too soon](#2-loading-and-running-code-too-soon)", calling `require()` is an expensive operation. If you are able to do so, bundle your application's code into a single file. -### Why? +#### Why? Modern JavaScript development usually involves many files and modules. While that's perfectly fine for developing with Electron, we heavily recommend that you bundle all your code into one single file to ensure that the overhead included in calling `require()` is only paid once when your application loads. -### How? +#### How? There are numerous JavaScript bundlers out there and we know better than to anger the community by recommending one tool over another. We do however diff --git a/docs/tutorial/security.md b/docs/tutorial/security.md index 1fd876827b..688dda5a68 100644 --- a/docs/tutorial/security.md +++ b/docs/tutorial/security.md @@ -1,6 +1,24 @@ -# Security, Native Capabilities, and Your Responsibility +--- +title: Security +description: A set of guidelines for building secure Electron apps +slug: security +hide_title: true +toc_max_heading_level: 3 +--- +# Security -As web developers, we usually enjoy the strong security net of the browser - +:::info Reporting security issues +For information on how to properly disclose an Electron vulnerability, +see [SECURITY.md](https://github.com/electron/electron/tree/main/SECURITY.md). + +For upstream Chromium vulnerabilities: Electron keeps up to date with alternating +Chromium releases. For more information, see the +[Electron Release Timelines](../tutorial/electron-timelines.md) document. +::: + +## Preface + +As web developers, we usually enjoy the strong security net of the browser — the risks associated with the code we write are relatively small. Our websites are granted limited powers in a sandbox, and we trust that our users enjoy a browser built by a large team of engineers that is able to quickly respond to @@ -17,20 +35,12 @@ With that in mind, be aware that displaying arbitrary content from untrusted sources poses a severe security risk that Electron is not intended to handle. In fact, the most popular Electron apps (Atom, Slack, Visual Studio Code, etc) display primarily local content (or trusted, secure remote content without Node -integration) – if your application executes code from an online source, it is +integration) — if your application executes code from an online source, it is your responsibility to ensure that the code is not malicious. -## Reporting Security Issues +## General guidelines -For information on how to properly disclose an Electron vulnerability, -see [SECURITY.md](https://github.com/electron/electron/tree/main/SECURITY.md) - -## Chromium Security Issues and Upgrades - -Electron keeps up to date with alternating Chromium releases. For more information, -see the [Electron Release Cadence blog post](https://electronjs.org/blog/12-week-cadence). - -## Security Is Everyone's Responsibility +### Security is everyone's responsibility It is important to remember that the security of your Electron application is the result of the overall security of the framework foundation @@ -56,7 +66,7 @@ is your own code. Common web vulnerabilities, such as Cross-Site Scripting (XSS) have a higher security impact on Electron applications hence it is highly recommended to adopt secure software development best practices and perform security testing. -## Isolation For Untrusted Content +### Isolation for untrusted content A security issue exists whenever you receive code from an untrusted source (e.g. a remote server) and execute it locally. As an example, consider a remote @@ -65,72 +75,74 @@ an attacker somehow manages to change said content (either by attacking the source directly, or by sitting between your app and the actual destination), they will be able to execute native code on the user's machine. -> :warning: Under no circumstances should you load and execute remote code with +:::warning +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] tag or [`BrowserView`][browser-view], make sure to disable the `nodeIntegration` and enable `contextIsolation`. +::: -## Electron Security Warnings - -From Electron 2.0 on, developers will see warnings and recommendations printed -to the developer console. They only show up when the binary's name is Electron, -indicating that a developer is currently looking at the console. +:::info Electron security warnings +Security warnings and recommendations are printed to the developer console. +They only show up when the binary's name is Electron, indicating that a developer +is currently looking at the console. 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. +::: -## Checklist: Security Recommendations +## Checklist: Security recommendations You should at least follow these steps to improve the security of your application: 1. [Only load secure content](#1-only-load-secure-content) 2. [Disable the Node.js integration in all renderers that display remote content](#2-do-not-enable-nodejs-integration-for-remote-content) 3. [Enable context isolation in all renderers that display remote content](#3-enable-context-isolation-for-remote-content) -4. [Enable sandboxing](#4-enable-sandboxing) +4. [Enable process sandboxing](#4-enable-process-sandboxing) 5. [Use `ses.setPermissionRequestHandler()` in all sessions that load remote content](#5-handle-session-permission-requests-from-remote-content) 6. [Do not disable `webSecurity`](#6-do-not-disable-websecurity) 7. [Define a `Content-Security-Policy`](#7-define-a-content-security-policy) and use restrictive rules (i.e. `script-src 'self'`) -8. [Do not set `allowRunningInsecureContent` to `true`](#8-do-not-set-allowrunninginsecurecontent-to-true) +8. [Do not enable `allowRunningInsecureContent`](#8-do-not-enable-allowrunninginsecurecontent) 9. [Do not enable experimental features](#9-do-not-enable-experimental-features) 10. [Do not use `enableBlinkFeatures`](#10-do-not-use-enableblinkfeatures) -11. [``: Do not use `allowpopups`](#11-do-not-use-allowpopups) +11. [``: Do not use `allowpopups`](#11-do-not-use-allowpopups-for-webviews) 12. [``: Verify options and params](#12-verify-webview-options-before-creation) 13. [Disable or limit navigation](#13-disable-or-limit-navigation) 14. [Disable or limit creation of new windows](#14-disable-or-limit-creation-of-new-windows) -15. [Do not use `openExternal` with untrusted content](#15-do-not-use-openexternal-with-untrusted-content) +15. [Do not use `shell.openExternal` with untrusted content](#15-do-not-use-shellopenexternal-with-untrusted-content) 16. [Use a current version of Electron](#16-use-a-current-version-of-electron) To automate the detection of misconfigurations and insecure patterns, it is possible to use -[electronegativity](https://github.com/doyensec/electronegativity). For +[Electronegativity](https://github.com/doyensec/electronegativity). For additional details on potential weaknesses and implementation bugs when developing applications using Electron, please refer to this [guide for -developers and auditors](https://doyensec.com/resources/us-17-Carettoni-Electronegativity-A-Study-Of-Electron-Security-wp.pdf) +developers and auditors](https://doyensec.com/resources/us-17-Carettoni-Electronegativity-A-Study-Of-Electron-Security-wp.pdf). -## 1) Only Load Secure Content +### 1. Only load secure content Any resources not included with your application should be loaded using a secure protocol like `HTTPS`. In other words, do not use insecure protocols like `HTTP`. Similarly, we recommend the use of `WSS` over `WS`, `FTPS` over `FTP`, and so on. -### Why? +#### Why? `HTTPS` has three main benefits: -1) It authenticates the remote server, ensuring your app connects to the correct +1. It authenticates the remote server, ensuring your app connects to the correct host instead of an impersonator. -2) It ensures data integrity, asserting that the data was not modified while in +1. It ensures data integrity, asserting that the data was not modified while in transit between your application and the host. -3) It encrypts the traffic between your user and the destination host, making it +1. It encrypts the traffic between your user and the destination host, making it more difficult to eavesdrop on the information sent between your app and the host. -### How? +#### How? -```js +```js title='main.js (Main Process)' // Bad browserWindow.loadURL('http://example.com') @@ -138,7 +150,7 @@ browserWindow.loadURL('http://example.com') browserWindow.loadURL('https://example.com') ``` -```html +```html title='index.html (Renderer Process)' @@ -148,9 +160,11 @@ browserWindow.loadURL('https://example.com') ``` -## 2) Do not enable Node.js Integration for Remote Content +### 2. Do not enable Node.js integration for remote content -_This recommendation is the default behavior in Electron since 5.0.0._ +:::info +This recommendation is the default behavior in Electron since 5.0.0. +::: It is paramount that you do not enable Node.js integration in any renderer ([`BrowserWindow`][browser-window], [`BrowserView`][browser-view], or @@ -163,7 +177,7 @@ After this, you can grant additional permissions for specific hosts. For example if you are opening a BrowserWindow pointed at `https://example.com/`, you can give that website exactly the abilities it needs, but no more. -### Why? +#### Why? A cross-site-scripting (XSS) attack is more dangerous if an attacker can jump out of the renderer process and execute code on the user's computer. @@ -172,12 +186,13 @@ power is usually limited to messing with the website that they are executed on. Disabling Node.js integration helps prevent an XSS from being escalated into a so-called "Remote Code Execution" (RCE) attack. -### How? +#### How? -```js +```js title='main.js (Main Process)' // Bad const mainWindow = new BrowserWindow({ webPreferences: { + contextIsolation: false, nodeIntegration: true, nodeIntegrationInWorker: true } @@ -186,7 +201,7 @@ const mainWindow = new BrowserWindow({ mainWindow.loadURL('https://example.com') ``` -```js +```js title='main.js (Main Process)' // Good const mainWindow = new BrowserWindow({ webPreferences: { @@ -197,7 +212,7 @@ const mainWindow = new BrowserWindow({ mainWindow.loadURL('https://example.com') ``` -```html +```html title='index.html (Renderer Process)' @@ -208,21 +223,13 @@ mainWindow.loadURL('https://example.com') When disabling Node.js integration, you can still expose APIs to your website that do consume Node.js modules or features. Preload scripts continue to have access to `require` and other Node.js features, allowing developers to expose a custom -API to remotely loaded content. +API to remotely loaded content via the [contextBridge API](../api/context-bridge.md). -In the following example preload script, the later loaded website will have -access to a `window.readConfig()` method, but no Node.js features. +### 3. Enable Context Isolation for remote content -```js -const { readFileSync } = require('fs') - -window.readConfig = () => { - const data = readFileSync('./config.json') - return data -} -``` - -## 3) Enable Context Isolation for Remote Content +:::info +This recommendation is the default behavior in Electron since 12.0.0. +::: Context isolation is an Electron feature that allows developers to run code in preload scripts and in Electron APIs in a dedicated JavaScript context. In @@ -235,48 +242,42 @@ to enable this behavior. Even when `nodeIntegration: false` is used, to truly enforce strong isolation and prevent the use of Node primitives `contextIsolation` **must** also be used. -### Why & How? - +:::info For more information on what `contextIsolation` is and how to enable it please see our dedicated [Context Isolation](context-isolation.md) document. +:::info -## 4) Enable Sandboxing +### 4. Enable process sandboxing -[Sandboxing](sandbox.md) is a Chromium feature that uses the operating system to +[Sandboxing](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/design/sandbox.md) +is a Chromium feature that uses the operating system to significantly limit what renderer processes have access to. You should enable the sandbox in all renderers. Loading, reading or processing any untrusted content in an unsandboxed process, including the main process, is not advised. -### How? +:::info +For more information on what `contextIsolation` is and how to enable it please +see our dedicated [Process Sandboxing](sandbox.md) document. +:::info -When creating a window, pass the `sandbox: true` option in `webPreferences`: +### 5. Handle session permission requests from remote content -```js -const win = new BrowserWindow({ - webPreferences: { - sandbox: true - } -}) -``` - -## 5) Handle Session Permission Requests From Remote Content - -You may have seen permission requests while using Chrome: They pop up whenever +You may have seen permission requests while using Chrome: they pop up whenever the website attempts to use a feature that the user has to manually approve ( like notifications). The API is based on the [Chromium permissions API](https://developer.chrome.com/extensions/permissions) and implements the same types of permissions. -### Why? +#### Why? By default, Electron will automatically approve all permission requests unless the developer has manually configured a custom handler. While a solid default, security-conscious developers might want to assume the very opposite. -### How? +#### How? -```js +```js title='main.js (Main Process)' const { session } = require('electron') session @@ -297,9 +298,11 @@ session }) ``` -## 6) Do Not Disable WebSecurity +### 6. Do not disable `webSecurity` -_Recommendation is Electron's default_ +:::info +This recommendation is Electron's default. +::: You may have already guessed that disabling the `webSecurity` property on a renderer process ([`BrowserWindow`][browser-window], @@ -308,15 +311,15 @@ security features. Do not disable `webSecurity` in production applications. -### Why? +#### Why? Disabling `webSecurity` will disable the same-origin policy and set `allowRunningInsecureContent` property to `true`. In other words, it allows the execution of insecure code from different domains. -### How? +#### How? -```js +```js title='main.js (Main Process)' // Bad const mainWindow = new BrowserWindow({ webPreferences: { @@ -325,12 +328,12 @@ const mainWindow = new BrowserWindow({ }) ``` -```js +```js title='main.js (Main Process)' // Good const mainWindow = new BrowserWindow() ``` -```html +```html title='index.html (Renderer Process)' @@ -338,13 +341,13 @@ const mainWindow = new BrowserWindow() ``` -## 7) Define a Content Security Policy +### 7. Define a Content Security Policy A Content Security Policy (CSP) is an additional layer of protection against cross-site-scripting attacks and data injection attacks. We recommend that they be enabled by any website you load inside Electron. -### Why? +#### Why? CSP allows the server serving content to restrict and control the resources Electron can load for that given web page. `https://example.com` should @@ -352,6 +355,8 @@ be allowed to load scripts from the origins you defined while scripts from `https://evil.attacker.com` should not be allowed to run. Defining a CSP is an easy way to improve your application's security. +#### How? + The following CSP will allow Electron to execute scripts from the current website and from `apis.example.com`. @@ -363,14 +368,14 @@ Content-Security-Policy: '*' Content-Security-Policy: script-src 'self' https://apis.example.com ``` -### CSP HTTP Header +#### CSP HTTP headers Electron respects the [`Content-Security-Policy` HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) which can be set using Electron's [`webRequest.onHeadersReceived`](../api/web-request.md#webrequestonheadersreceivedfilter-listener) handler: -```javascript +```javascript title='main.js (Main Process)' const { session } = require('electron') session.defaultSession.webRequest.onHeadersReceived((details, callback) => { @@ -383,20 +388,22 @@ session.defaultSession.webRequest.onHeadersReceived((details, callback) => { }) ``` -### CSP Meta Tag +#### CSP meta tag -CSP's preferred delivery mechanism is an HTTP header, however it is not possible +CSP's preferred delivery mechanism is an HTTP header. However, it is not possible to use this method when loading a resource using the `file://` protocol. It can -be useful in some cases, such as using the `file://` protocol, to set a policy -on a page directly in the markup using a `` tag: +be useful in some cases to set a policy on a page directly in the markup using a +`` tag: -```html +```html title='index.html (Renderer Process)' ``` -## 8) Do Not Set `allowRunningInsecureContent` to `true` +### 8. Do not enable `allowRunningInsecureContent` -_Recommendation is Electron's default_ +:::info +This recommendation is Electron's default. +::: By default, Electron will not allow websites loaded over `HTTPS` to load and execute scripts, CSS, or plugins from insecure sources (`HTTP`). Setting the @@ -405,15 +412,15 @@ property `allowRunningInsecureContent` to `true` disables that protection. Loading the initial HTML of a website over `HTTPS` and attempting to load subsequent resources via `HTTP` is also known as "mixed content". -### Why? +#### Why? Loading content over `HTTPS` assures the authenticity and integrity of the loaded resources while encrypting the traffic itself. See the section on [only displaying secure content](#1-only-load-secure-content) for more details. -### How? +#### How? -```js +```js title='main.js (Main Process)' // Bad const mainWindow = new BrowserWindow({ webPreferences: { @@ -422,19 +429,21 @@ const mainWindow = new BrowserWindow({ }) ``` -```js +```js title='main.js (Main Process)' // Good const mainWindow = new BrowserWindow({}) ``` -## 9) Do Not Enable Experimental Features +### 9. Do not enable experimental features -_Recommendation is Electron's default_ +:::info +This recommendation is Electron's default. +::: Advanced users of Electron can enable experimental Chromium features using the `experimentalFeatures` property. -### Why? +#### Why? Experimental features are, as the name suggests, experimental and have not been enabled for all Chromium users. Furthermore, their impact on Electron as a whole @@ -443,9 +452,9 @@ has likely not been tested. Legitimate use cases exist, but unless you know what you are doing, you should not enable this property. -### How? +#### How? -```js +```js title='main.js (Main Process)' // Bad const mainWindow = new BrowserWindow({ webPreferences: { @@ -454,20 +463,22 @@ const mainWindow = new BrowserWindow({ }) ``` -```js +```js title='main.js (Main Process)' // Good const mainWindow = new BrowserWindow({}) ``` -## 10) Do Not Use `enableBlinkFeatures` +### 10. Do not use `enableBlinkFeatures` -_Recommendation is Electron's default_ +:::info +This recommendation is Electron's default. +::: Blink is the name of the rendering engine behind Chromium. As with `experimentalFeatures`, the `enableBlinkFeatures` property allows developers to enable features that have been disabled by default. -### Why? +#### Why? Generally speaking, there are likely good reasons if a feature was not enabled by default. Legitimate use cases for enabling specific features exist. As a @@ -475,9 +486,9 @@ developer, you should know exactly why you need to enable a feature, what the ramifications are, and how it impacts the security of your application. Under no circumstances should you enable features speculatively. -### How? +#### How? -```js +```js title='main.js (Main Process)' // Bad const mainWindow = new BrowserWindow({ webPreferences: { @@ -486,14 +497,16 @@ const mainWindow = new BrowserWindow({ }) ``` -```js +```js title='main.js (Main Process)' // Good const mainWindow = new BrowserWindow() ``` -## 11) Do Not Use `allowpopups` +### 11. Do not use `allowpopups` for WebViews -_Recommendation is Electron's default_ +:::info +This recommendation is Electron's default. +::: If you are using [``][webview-tag], you might need the pages and scripts loaded in your `` tag to open new windows. The `allowpopups` attribute @@ -501,16 +514,16 @@ enables them to create new [`BrowserWindows`][browser-window] using the `window.open()` method. `` tags are otherwise not allowed to create new windows. -### Why? +#### Why? If you do not need popups, you are better off not allowing the creation of new [`BrowserWindows`][browser-window] by default. This follows the principle of minimally required access: Don't let a website create new popups unless you know it needs that feature. -### How? +#### How? -```html +```html title='index.html (Renderer Process)' @@ -518,7 +531,7 @@ you know it needs that feature. ``` -## 12) Verify WebView Options Before Creation +### 12. Verify WebView options before creation A WebView created in a renderer process that does not have Node.js integration enabled will not be able to enable integration itself. However, a WebView will @@ -528,7 +541,7 @@ It is a good idea to control the creation of new [``][webview-tag] tags from the main process and to verify that their webPreferences do not disable security features. -### Why? +#### Why? Since `` live in the DOM, they can be created by a script running on your website even if Node.js integration is otherwise disabled. @@ -538,13 +551,13 @@ a renderer process. In most cases, developers do not need to disable any of those features - and you should therefore not allow different configurations for newly created [``][webview-tag] tags. -### How? +#### How? Before a [``][webview-tag] tag is attached, Electron will fire the `will-attach-webview` event on the hosting `webContents`. Use the event to prevent the creation of `webViews` with possibly insecure options. -```js +```js title='main.js (Main Process)' app.on('web-contents-created', (event, contents) => { contents.on('will-attach-webview', (event, webPreferences, params) => { // Strip away preload scripts if unused or verify their location is legitimate @@ -562,16 +575,16 @@ app.on('web-contents-created', (event, contents) => { }) ``` -Again, this list merely minimizes the risk, it does not remove it. If your goal +Again, this list merely minimizes the risk, but does not remove it. If your goal is to display a website, a browser will be a more secure option. -## 13) Disable or limit navigation +### 13. Disable or limit navigation If your app has no need to navigate or only needs to navigate to known pages, it is a good idea to limit navigation outright to that known scope, disallowing any other kinds of navigation. -### Why? +#### Why? Navigation is a common attack vector. If an attacker can convince your app to navigate away from its current page, they can possibly force your app to open @@ -584,7 +597,7 @@ A common attack pattern is that the attacker convinces your app's users to interact with the app in such a way that it navigates to one of the attacker's pages. This is usually done via links, plugins, or other user-generated content. -### How? +#### How? If your app has no need for navigation, you can call `event.preventDefault()` in a [`will-navigate`][will-navigate] handler. If you know which pages your app @@ -595,7 +608,7 @@ We recommend that you use Node's parser for URLs. Simple string comparisons can sometimes be fooled - a `startsWith('https://example.com')` test would let `https://example.com.attacker.com` through. -```js +```js title='main.js (Main Process)' const URL = require('url').URL app.on('web-contents-created', (event, contents) => { @@ -609,12 +622,12 @@ app.on('web-contents-created', (event, contents) => { }) ``` -## 14) Disable or limit creation of new windows +### 14. Disable or limit creation of new windows If you have a known set of windows, it's a good idea to limit the creation of additional windows in your app. -### Why? +#### Why? Much like navigation, the creation of new `webContents` is a common attack vector. Attackers attempt to convince your app to create new windows, frames, @@ -627,7 +640,7 @@ security at no cost. This is commonly the case for apps that open one `BrowserWindow` and do not need to open an arbitrary number of additional windows at runtime. -### How? +#### How? [`webContents`][web-contents] will delegate to its [window open handler][window-open-handler] before creating new windows. The handler will @@ -635,7 +648,7 @@ receive, amongst other parameters, the `url` the window was requested to open and the options used to create it. We recommend that you register a handler to monitor the creation of windows, and deny any unexpected window creation. -```js +```js title='main.js (Main Process)' const { shell } = require('electron') app.on('web-contents-created', (event, contents) => { @@ -656,40 +669,40 @@ app.on('web-contents-created', (event, contents) => { }) ``` -## 15) Do not use `openExternal` with untrusted content +### 15. Do not use `shell.openExternal` with untrusted content -Shell's [`openExternal`][open-external] allows opening a given protocol URI with -the desktop's native utilities. On macOS, for instance, this function is similar -to the `open` terminal command utility and will open the specific application -based on the URI and filetype association. +The shell module's [`openExternal`][open-external] API allows opening a given +protocol URI with the desktop's native utilities. On macOS, for instance, this +function is similar to the `open` terminal command utility and will open the +specific application based on the URI and filetype association. -### Why? +#### Why? Improper use of [`openExternal`][open-external] can be leveraged to compromise the user's host. When openExternal is used with untrusted content, it can be leveraged to execute arbitrary commands. -### How? +#### How? -```js +```js title='main.js (Main Process)' // Bad const { shell } = require('electron') shell.openExternal(USER_CONTROLLED_DATA_HERE) ``` -```js +```js title='main.js (Main Process)' // Good const { shell } = require('electron') shell.openExternal('https://example.com/index.html') ``` -## 16) Use a current version of Electron +### 16. Use a current version of Electron You should strive for always using the latest available version of Electron. Whenever a new major version is released, you should attempt to update your app as quickly as possible. -### Why? +#### Why? An application built with an older version of Electron, Chromium, and Node.js is an easier target than an application that is using more recent versions of @@ -705,6 +718,13 @@ to fix issues before publishing them. Your application will be more secure if it is running a recent version of Electron (and thus, Chromium and Node.js) for which potential security issues are not as widely known. +#### How? + +Migrate your app one major version at a time, while referring to Electron's +[Breaking Changes][breaking-changes] document to see if any code needs to +be updated. + +[breaking-changes]: ../breaking-changes.md [browser-window]: ../api/browser-window.md [browser-view]: ../api/browser-view.md [webview-tag]: ../api/webview-tag.md @@ -712,5 +732,4 @@ which potential security issues are not as widely known. [window-open-handler]: ../api/web-contents.md#contentssetwindowopenhandlerhandler [will-navigate]: ../api/web-contents.md#event-will-navigate [open-external]: ../api/shell.md#shellopenexternalurl-options -[sandbox]: ../tutorial/sandbox.md [responsible-disclosure]: https://en.wikipedia.org/wiki/Responsible_disclosure