docs: Improved security doc, particularly around isolation and tool (#16703)
* Improved security doc, particularly around isolation and tool * Fixes as suggested by @ckerr * libcc update * fixing lint stuff
This commit is contained in:
parent
a07cb2afd7
commit
1bbb47be5b
1 changed files with 94 additions and 28 deletions
|
@ -40,19 +40,46 @@ interested in hearing more about specific use cases from the people that build
|
||||||
things on top of Electron. Pull requests and contributions supporting this
|
things on top of Electron. Pull requests and contributions supporting this
|
||||||
effort are always very welcome.
|
effort are always very welcome.
|
||||||
|
|
||||||
## Ignoring Above Advice
|
## Security Is Everyone's Responsibility
|
||||||
|
|
||||||
A security issue exists whenever you receive code from a remote destination and
|
It is important to remember that the security of your Electron application is
|
||||||
execute it locally. As an example, consider a remote website being displayed
|
the result of the overall security of the framework foundation
|
||||||
inside a [`BrowserWindow`][browser-window]. If an attacker somehow manages to
|
(*Chromium*, *Node.js*), Electron itself, all NPM dependencies and
|
||||||
change said content (either by attacking the source directly, or by sitting
|
your code. As such, it is your responsibility to follow a few important best
|
||||||
between your app and the actual destination), they will be able to execute
|
practices:
|
||||||
native code on the user's machine.
|
|
||||||
|
* **Keep your application up-to-date with the latest Electron framework release.**
|
||||||
|
When releasing your product, you’re also shipping a bundle composed of Electron,
|
||||||
|
Chromium shared library and Node.js. Vulnerabilities affecting these components
|
||||||
|
may impact the security of your application. By updating Electron to the latest
|
||||||
|
version, you ensure that critical vulnerabilities (such as *nodeIntegration bypasses*)
|
||||||
|
are already patched and cannot be exploited in your application.
|
||||||
|
|
||||||
|
* **Evaluate your dependencies.** While NPM provides half a million reusable packages,
|
||||||
|
it is your responsibility to choose trusted 3rd-party libraries. If you use outdated
|
||||||
|
libraries affected by known vulnerabilities or rely on poorly maintained code,
|
||||||
|
your application security could be in jeopardy.
|
||||||
|
|
||||||
|
* **Adopt secure coding practices.** The first line of defense for your application
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
website being displayed inside a default [`BrowserWindow`][browser-window]. If
|
||||||
|
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
|
Node.js integration enabled. Instead, use only local files (packaged together
|
||||||
with your application) to execute Node.js code. To display remote content, use
|
with your application) to execute Node.js code. To display remote content, use
|
||||||
the [`<webview>`][webview-tag] tag and make sure to disable the `nodeIntegration`.
|
the [`<webview>`][webview-tag] tag or [`BrowserView`][browser-view], make sure
|
||||||
|
to disable the `nodeIntegration` and enable `contextIsolation`.
|
||||||
|
|
||||||
## Electron Security Warnings
|
## Electron Security Warnings
|
||||||
|
|
||||||
|
@ -66,8 +93,7 @@ either `process.env` or the `window` object.
|
||||||
|
|
||||||
## Checklist: Security Recommendations
|
## Checklist: Security Recommendations
|
||||||
|
|
||||||
This is not bulletproof, but at the least, you should follow these steps to
|
You should at least follow these steps to improve the security of your application:
|
||||||
improve the security of your application.
|
|
||||||
|
|
||||||
1. [Only load secure content](#1-only-load-secure-content)
|
1. [Only load secure content](#1-only-load-secure-content)
|
||||||
2. [Disable the Node.js integration in all renderers that display remote content](#2-disable-nodejs-integration-for-remote-content)
|
2. [Disable the Node.js integration in all renderers that display remote content](#2-disable-nodejs-integration-for-remote-content)
|
||||||
|
@ -82,6 +108,14 @@ improve the security of your application.
|
||||||
11. [`<webview>`: Verify options and params](#11-verify-webview-options-before-creation)
|
11. [`<webview>`: Verify options and params](#11-verify-webview-options-before-creation)
|
||||||
12. [Disable or limit navigation](#12-disable-or-limit-navigation)
|
12. [Disable or limit navigation](#12-disable-or-limit-navigation)
|
||||||
13. [Disable or limit creation of new windows](#13-disable-or-limit-creation-of-new-windows)
|
13. [Disable or limit creation of new windows](#13-disable-or-limit-creation-of-new-windows)
|
||||||
|
14. [Do not use `openExternal` with untrusted content](#14-do-not-use-openexternal-with-untrusted-content)
|
||||||
|
|
||||||
|
To automate the detection of misconfigurations and insecure patterns, it is
|
||||||
|
possible to use
|
||||||
|
[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)
|
||||||
|
|
||||||
## 1) Only Load Secure Content
|
## 1) Only Load Secure Content
|
||||||
|
|
||||||
|
@ -106,20 +140,20 @@ like `HTTP`. Similarly, we recommend the use of `WSS` over `WS`, `FTPS` over
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Bad
|
// Bad
|
||||||
browserWindow.loadURL('http://my-website.com')
|
browserWindow.loadURL('http://example.com')
|
||||||
|
|
||||||
// Good
|
// Good
|
||||||
browserWindow.loadURL('https://my-website.com')
|
browserWindow.loadURL('https://example.com')
|
||||||
```
|
```
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!-- Bad -->
|
<!-- Bad -->
|
||||||
<script crossorigin src="http://cdn.com/react.js"></script>
|
<script crossorigin src="http://example.com/react.js"></script>
|
||||||
<link rel="stylesheet" href="http://cdn.com/style.css">
|
<link rel="stylesheet" href="http://example.com/style.css">
|
||||||
|
|
||||||
<!-- Good -->
|
<!-- Good -->
|
||||||
<script crossorigin src="https://cdn.com/react.js"></script>
|
<script crossorigin src="https://example.com/react.js"></script>
|
||||||
<link rel="stylesheet" href="https://cdn.com/style.css">
|
<link rel="stylesheet" href="https://example.com/style.css">
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -133,7 +167,7 @@ for an attacker to harm your users should they gain the ability to execute
|
||||||
JavaScript on your website.
|
JavaScript on your website.
|
||||||
|
|
||||||
After this, you can grant additional permissions for specific hosts. For example,
|
After this, you can grant additional permissions for specific hosts. For example,
|
||||||
if you are opening a BrowserWindow pointed at `https://my-website.com/", you can
|
if you are opening a BrowserWindow pointed at `https://example.com/", you can
|
||||||
give that website exactly the abilities it needs, but no more.
|
give that website exactly the abilities it needs, but no more.
|
||||||
|
|
||||||
### Why?
|
### Why?
|
||||||
|
@ -150,7 +184,7 @@ so-called "Remote Code Execution" (RCE) attack.
|
||||||
```js
|
```js
|
||||||
// Bad
|
// Bad
|
||||||
const mainWindow = new BrowserWindow()
|
const mainWindow = new BrowserWindow()
|
||||||
mainWindow.loadURL('https://my-website.com')
|
mainWindow.loadURL('https://example.com')
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
@ -158,11 +192,12 @@ mainWindow.loadURL('https://my-website.com')
|
||||||
const mainWindow = new BrowserWindow({
|
const mainWindow = new BrowserWindow({
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
|
nodeIntegrationInWorker: false,
|
||||||
preload: './preload.js'
|
preload: './preload.js'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
mainWindow.loadURL('https://my-website.com')
|
mainWindow.loadURL('https://example.com')
|
||||||
```
|
```
|
||||||
|
|
||||||
```html
|
```html
|
||||||
|
@ -201,6 +236,9 @@ practice, that means that global objects like `Array.prototype.push` or
|
||||||
Electron uses the same technology as Chromium's [Content Scripts](https://developer.chrome.com/extensions/content_scripts#execution-environment)
|
Electron uses the same technology as Chromium's [Content Scripts](https://developer.chrome.com/extensions/content_scripts#execution-environment)
|
||||||
to enable this behavior.
|
to enable this behavior.
|
||||||
|
|
||||||
|
Even when you use `nodeIntegration: false` to enforce strong isolation and
|
||||||
|
prevent the use of Node primitives, `contextIsolation` must also be used.
|
||||||
|
|
||||||
### Why?
|
### Why?
|
||||||
|
|
||||||
Context isolation allows each the scripts on running in the renderer to make
|
Context isolation allows each the scripts on running in the renderer to make
|
||||||
|
@ -209,7 +247,7 @@ the scripts in the Electron API or the preload script.
|
||||||
|
|
||||||
While still an experimental Electron feature, context isolation adds an
|
While still an experimental Electron feature, context isolation adds an
|
||||||
additional layer of security. It creates a new JavaScript world for Electron
|
additional layer of security. It creates a new JavaScript world for Electron
|
||||||
APIs and preload scripts.
|
APIs and preload scripts, which mitigates so-called "Prototype Pollution" attacks.
|
||||||
|
|
||||||
At the same time, preload scripts still have access to the `document` and
|
At the same time, preload scripts still have access to the `document` and
|
||||||
`window` objects. In other words, you're getting a decent return on a likely
|
`window` objects. In other words, you're getting a decent return on a likely
|
||||||
|
@ -279,7 +317,7 @@ session
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify URL
|
// Verify URL
|
||||||
if (!url.startsWith('https://my-website.com/')) {
|
if (!url.startsWith('https://example.com/')) {
|
||||||
// Denies the permissions request
|
// Denies the permissions request
|
||||||
return callback(false)
|
return callback(false)
|
||||||
}
|
}
|
||||||
|
@ -337,20 +375,20 @@ be enabled by any website you load inside Electron.
|
||||||
### Why?
|
### Why?
|
||||||
|
|
||||||
CSP allows the server serving content to restrict and control the resources
|
CSP allows the server serving content to restrict and control the resources
|
||||||
Electron can load for that given web page. `https://your-page.com` should
|
Electron can load for that given web page. `https://example.com` should
|
||||||
be allowed to load scripts from the origins you defined while scripts from
|
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
|
`https://evil.attacker.com` should not be allowed to run. Defining a CSP is an
|
||||||
easy way to improve your application's security.
|
easy way to improve your application's security.
|
||||||
|
|
||||||
The following CSP will allow Electron to execute scripts from the current
|
The following CSP will allow Electron to execute scripts from the current
|
||||||
website and from `apis.mydomain.com`.
|
website and from `apis.example.com`.
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
// Bad
|
// Bad
|
||||||
Content-Security-Policy: '*'
|
Content-Security-Policy: '*'
|
||||||
|
|
||||||
// Good
|
// Good
|
||||||
Content-Security-Policy: script-src 'self' https://apis.mydomain.com
|
Content-Security-Policy: script-src 'self' https://apis.example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
### CSP HTTP Header
|
### CSP HTTP Header
|
||||||
|
@ -551,7 +589,7 @@ app.on('web-contents-created', (event, contents) => {
|
||||||
webPreferences.nodeIntegration = false
|
webPreferences.nodeIntegration = false
|
||||||
|
|
||||||
// Verify URL being loaded
|
// Verify URL being loaded
|
||||||
if (!params.src.startsWith('https://yourapp.com/')) {
|
if (!params.src.startsWith('https://example.com/')) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -588,8 +626,8 @@ might navigate to, check the URL in the event handler and only let navigation
|
||||||
occur if it matches the URLs you're expecting.
|
occur if it matches the URLs you're expecting.
|
||||||
|
|
||||||
We recommend that you use Node's parser for URLs. Simple string comparisons can
|
We recommend that you use Node's parser for URLs. Simple string comparisons can
|
||||||
sometimes be fooled - a `startsWith('https://google.com')` test would let
|
sometimes be fooled - a `startsWith('https://example.com')` test would let
|
||||||
`https://google.com.attacker.com` through.
|
`https://example.com.attacker.com` through.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const URL = require('url').URL
|
const URL = require('url').URL
|
||||||
|
@ -598,7 +636,7 @@ app.on('web-contents-created', (event, contents) => {
|
||||||
contents.on('will-navigate', (event, navigationUrl) => {
|
contents.on('will-navigate', (event, navigationUrl) => {
|
||||||
const parsedUrl = new URL(navigationUrl)
|
const parsedUrl = new URL(navigationUrl)
|
||||||
|
|
||||||
if (parsedUrl.origin !== 'https://my-own-server.com') {
|
if (parsedUrl.origin !== 'https://example.com') {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -645,9 +683,37 @@ app.on('web-contents-created', (event, contents) => {
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 14) Do not use `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.
|
||||||
|
|
||||||
|
### 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?
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Bad
|
||||||
|
const { shell } = require('electron')
|
||||||
|
shell.openExternal(USER_CONTROLLED_DATA_HERE)
|
||||||
|
```
|
||||||
|
```js
|
||||||
|
// Good
|
||||||
|
const { shell } = require('electron')
|
||||||
|
shell.openExternal('https://example.com/index.html')
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
[browser-window]: ../api/browser-window.md
|
[browser-window]: ../api/browser-window.md
|
||||||
[browser-view]: ../api/browser-view.md
|
[browser-view]: ../api/browser-view.md
|
||||||
[webview-tag]: ../api/webview-tag.md
|
[webview-tag]: ../api/webview-tag.md
|
||||||
[web-contents]: ../api/web-contents.md
|
[web-contents]: ../api/web-contents.md
|
||||||
[new-window]: ../api/web-contents.md#event-new-window
|
[new-window]: ../api/web-contents.md#event-new-window
|
||||||
[will-navigate]: ../api/web-contents.md#event-will-navigate
|
[will-navigate]: ../api/web-contents.md#event-will-navigate
|
||||||
|
[open-external]: ../api/shell.md#shellopenexternalurl-options-callback
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue