2016-05-06 10:23:18 +00:00
|
|
|
|
# Security, Native Capabilities, and Your Responsibility
|
|
|
|
|
|
2018-01-29 19:19:43 +00:00
|
|
|
|
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
|
|
|
|
|
newly discovered security threats.
|
2016-05-06 10:23:18 +00:00
|
|
|
|
|
2016-05-08 18:21:11 +00:00
|
|
|
|
When working with Electron, it is important to understand that Electron is not
|
|
|
|
|
a web browser. It allows you to build feature-rich desktop applications with
|
|
|
|
|
familiar web technologies, but your code wields much greater power. JavaScript
|
2016-06-01 05:36:45 +00:00
|
|
|
|
can access the filesystem, user shell, and more. This allows you to build
|
2018-01-29 19:19:43 +00:00
|
|
|
|
high quality native applications, but the inherent security risks scale with
|
|
|
|
|
the additional powers granted to your code.
|
2016-05-06 10:23:18 +00:00
|
|
|
|
|
2018-01-29 19:19:43 +00:00
|
|
|
|
With that in mind, be aware that displaying arbitrary content from un-trusted
|
2016-05-08 18:21:11 +00:00
|
|
|
|
sources poses a severe security risk that Electron is not intended to handle.
|
2016-05-10 08:41:16 +00:00
|
|
|
|
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
|
|
|
|
|
your responsibility to ensure that the code is not malicious.
|
2016-05-06 10:23:18 +00:00
|
|
|
|
|
2017-01-30 18:57:53 +00:00
|
|
|
|
## Reporting Security Issues
|
2017-01-30 18:49:17 +00:00
|
|
|
|
|
|
|
|
|
For information on how to properly disclose an Electron vulnerability,
|
|
|
|
|
see [SECURITY.md](https://github.com/electron/electron/tree/master/SECURITY.md)
|
|
|
|
|
|
2016-05-06 10:23:18 +00:00
|
|
|
|
## Chromium Security Issues and Upgrades
|
|
|
|
|
|
2016-05-10 08:41:16 +00:00
|
|
|
|
While Electron strives to support new versions of Chromium as soon as possible,
|
|
|
|
|
developers should be aware that upgrading is a serious undertaking - involving
|
|
|
|
|
hand-editing dozens or even hundreds of files. Given the resources and
|
|
|
|
|
contributions available today, Electron will often not be on the very latest
|
|
|
|
|
version of Chromium, lagging behind by either days or weeks.
|
2016-05-06 10:23:18 +00:00
|
|
|
|
|
2016-05-10 08:41:16 +00:00
|
|
|
|
We feel that our current system of updating the Chromium component strikes an
|
2018-01-29 19:19:43 +00:00
|
|
|
|
appropriate balance between the resources we have available and the needs of
|
|
|
|
|
the majority of applications built on top of the framework. We definitely are
|
2016-05-10 08:41:16 +00:00
|
|
|
|
interested in hearing more about specific use cases from the people that build
|
|
|
|
|
things on top of Electron. Pull requests and contributions supporting this
|
|
|
|
|
effort are always very welcome.
|
2016-05-06 10:23:18 +00:00
|
|
|
|
|
|
|
|
|
## Ignoring Above Advice
|
2016-09-08 17:39:29 +00:00
|
|
|
|
|
2016-05-10 08:41:16 +00:00
|
|
|
|
A security issue exists whenever you receive code from a remote destination and
|
|
|
|
|
execute it locally. As an example, consider a remote website being displayed
|
2018-01-29 19:19:43 +00:00
|
|
|
|
inside a `BrowserWindow`. If an attacker somehow manages to change said content
|
2016-05-10 08:41:16 +00:00
|
|
|
|
(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.
|
2016-05-06 10:23:18 +00:00
|
|
|
|
|
2016-05-10 08:41:16 +00:00
|
|
|
|
> :warning: Under no circumstances should you load and execute remote code with
|
2018-01-29 19:19:43 +00:00
|
|
|
|
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 and make sure to disable the `nodeIntegration`.
|
2016-05-06 10:23:18 +00:00
|
|
|
|
|
2018-01-29 00:55:11 +00:00
|
|
|
|
#### Checklist: Security Recommendations
|
2016-09-08 17:39:29 +00:00
|
|
|
|
|
2016-05-06 10:23:18 +00:00
|
|
|
|
This is not bulletproof, but at the least, you should attempt the following:
|
|
|
|
|
|
2018-01-29 00:55:11 +00:00
|
|
|
|
* [Only display secure (https) content](#only-display-secure-content)
|
2018-01-29 01:25:05 +00:00
|
|
|
|
* [Disable the Node integration in all renderers that display remote content](#disable-node-integration-for-remote-content)
|
2018-01-29 19:19:43 +00:00
|
|
|
|
* [Enable context isolation in all renderers that display remote content](#enable-context-isolation-for-remote-content)
|
|
|
|
|
* [Use `ses.setPermissionRequestHandler()` in all sessions that load remote content](#handle-session-permission-requests-from-remote-content)
|
|
|
|
|
* [Do not disable `webSecurity`](#do-not-disable-websecurity)
|
|
|
|
|
* [Define a `Content-Security-Policy`](#define-a-content-security-policy)
|
2016-06-16 22:19:38 +00:00
|
|
|
|
, and use restrictive rules (i.e. `script-src 'self'`)
|
2018-01-29 19:19:43 +00:00
|
|
|
|
* [Override and disable `eval`](#override-and-disable)
|
2016-05-08 18:21:11 +00:00
|
|
|
|
, which allows strings to be executed as code.
|
2018-01-29 19:19:43 +00:00
|
|
|
|
* [Do not set `allowRunningInsecureContent` to `true`](#do-not-set-allowRunningInsecureContent-to-true)
|
|
|
|
|
* [Do not enable experimental features](#do-not-enable-enable-experimental-features)
|
|
|
|
|
* [Do not use `blinkFeatures`](#do-not-use-blinkfeatures)
|
|
|
|
|
* [WebViews: Do not use `allowpopups`](#do-not-use-allowpopups)
|
2018-01-29 02:19:49 +00:00
|
|
|
|
* [WebViews: Verify the options and params of all `<webview>` tags](#verify-webview-options-before-creation)
|
2016-05-10 08:41:16 +00:00
|
|
|
|
|
2018-01-29 19:19:43 +00:00
|
|
|
|
|
2018-01-29 00:55:11 +00:00
|
|
|
|
## Only Display Secure Content
|
2018-01-29 19:19:43 +00:00
|
|
|
|
|
|
|
|
|
Any resources not included with your application should be loaded using a
|
|
|
|
|
secure protocol like `HTTPS`.
|
2018-01-29 00:55:11 +00:00
|
|
|
|
|
2018-01-29 01:25:05 +00:00
|
|
|
|
### Why?
|
2018-01-29 19:19:43 +00:00
|
|
|
|
|
2018-01-29 00:55:11 +00:00
|
|
|
|
`HTTPS` has three main benefits:
|
|
|
|
|
|
2018-01-29 19:19:43 +00:00
|
|
|
|
1) It authenticates the remote server, ensuring that the host is actually who
|
|
|
|
|
it claims to be. When loading a resource from an `HTTPS` host, it prevents
|
|
|
|
|
an attacker from impersonating that host, thus ensuring that the computer
|
|
|
|
|
your app's users are connecting to is actually the host you wanted them to
|
|
|
|
|
connect to.
|
2018-01-29 01:25:05 +00:00
|
|
|
|
2) It ensures data integrity, asserting that the data was not modified while in
|
|
|
|
|
transit between your application and the host.
|
|
|
|
|
3) It encryps the traffic between your user and the destination host, making it
|
|
|
|
|
more difficult to eavesdropping on the information sent between your app and
|
|
|
|
|
the host.
|
|
|
|
|
|
|
|
|
|
### How?
|
2018-01-29 19:19:43 +00:00
|
|
|
|
|
2018-01-29 00:55:11 +00:00
|
|
|
|
```js
|
|
|
|
|
// Bad
|
|
|
|
|
browserWindow.loadURL('http://my-website.com')
|
|
|
|
|
|
|
|
|
|
// Good
|
|
|
|
|
browserWindow.loadURL('https://my-website.com')
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```html
|
|
|
|
|
<!-- Bad -->
|
|
|
|
|
<script crossorigin src="http://cdn.com/react.js"></script>
|
|
|
|
|
<link rel="stylesheet" href="http://cdn.com/style.css">
|
|
|
|
|
|
|
|
|
|
<!-- Good -->
|
|
|
|
|
<script crossorigin src="https://cdn.com/react.js"></script>
|
|
|
|
|
<link rel="stylesheet" href="https://cdn.com/style.css">
|
|
|
|
|
```
|
|
|
|
|
|
2018-01-29 19:19:43 +00:00
|
|
|
|
|
2018-01-29 01:25:05 +00:00
|
|
|
|
## Disable Node Integration for Remote Content
|
|
|
|
|
|
2018-01-29 19:19:43 +00:00
|
|
|
|
It is paramount that you disable Node integration in any renderer
|
|
|
|
|
(`BrowserWindow`, `BrowserView`, or `WebView`) that loads remote content. The
|
|
|
|
|
goal of disabling Node integration is to limit the powers you grant to remote
|
|
|
|
|
content, thus making it dramatically more difficult for an attacker to harm
|
|
|
|
|
your users should they gain the ability to execute JavaScript on your website.
|
|
|
|
|
|
|
|
|
|
Disabling Node integration does not mean that you cannot grant additional
|
|
|
|
|
powers to the website you are loading. If you are opening a `BrowserWindow`
|
|
|
|
|
pointed at `https://my-website.com`, the goal is to give that website exactly
|
|
|
|
|
the abilities it needs, but no more.
|
2018-01-29 01:25:05 +00:00
|
|
|
|
|
|
|
|
|
### Why?
|
2018-01-29 19:19:43 +00:00
|
|
|
|
|
2018-01-29 02:19:49 +00:00
|
|
|
|
A cross-site-scripting (XSS) attack becomes dramatically more dangerous if an
|
|
|
|
|
attacker can jump out of the renderer process and execute code on the user's
|
|
|
|
|
computer. Cross-site-scripting attacks are fairly common - and while an issue,
|
2018-01-29 19:19:43 +00:00
|
|
|
|
their power is usually limited to messing with the website that they are
|
|
|
|
|
executed on. However, in a renderer process with Node.js integration enabled,
|
|
|
|
|
an XSS attack becomes a whole different class of attack: A so-called "Remote
|
|
|
|
|
Code Execution" (RCE) attack. Disabling Node.js integration limits the power
|
|
|
|
|
of successful XSS attacks.
|
2018-01-29 01:25:05 +00:00
|
|
|
|
|
|
|
|
|
### How?
|
2018-01-29 19:19:43 +00:00
|
|
|
|
|
2018-01-29 01:25:05 +00:00
|
|
|
|
```js
|
|
|
|
|
// Bad
|
|
|
|
|
const mainWindow = new BrowserWindow()
|
|
|
|
|
mainWindow.loadURL('https://my-website.com')
|
|
|
|
|
|
|
|
|
|
// Good
|
|
|
|
|
const mainWindow = new BrowserWindow({
|
|
|
|
|
webPreferences: {
|
|
|
|
|
nodeIntegration: false,
|
|
|
|
|
preload: './preload.js'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
mainWindow.loadURL('https://my-website.com')
|
|
|
|
|
```
|
|
|
|
|
|
2018-01-29 19:19:43 +00:00
|
|
|
|
```html
|
|
|
|
|
<!-- Bad -->
|
|
|
|
|
<webview nodeIntegration src="page.html"></webview>
|
2018-01-29 01:25:05 +00:00
|
|
|
|
|
2018-01-29 19:19:43 +00:00
|
|
|
|
<!-- Good -->
|
|
|
|
|
<webview src="page.html"></webview>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
When disabling Node 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.
|
|
|
|
|
|
|
|
|
|
In the following example preload script, the later loaded website will have
|
|
|
|
|
access to a `window.readConfig()` method, but no Node.js features.
|
2018-01-29 01:25:05 +00:00
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const { readFileSync } = require('fs')
|
|
|
|
|
|
|
|
|
|
window.readConfig = function () {
|
|
|
|
|
const data = readFileSync('./config.json')
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-01-29 19:19:21 +00:00
|
|
|
|
|
2018-01-29 19:01:22 +00:00
|
|
|
|
## Enable Context Isolation for Remote Content
|
|
|
|
|
|
2018-01-29 19:19:21 +00:00
|
|
|
|
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
|
|
|
|
|
practice, that means that global objects like `Array.prototype.push` or
|
|
|
|
|
`JSON.parse` cannot be modified by scripts running in the renderer process.
|
|
|
|
|
|
|
|
|
|
Electron uses the same technology as Chromium's [Content Scripts](https://developer.chrome.com/extensions/content_scripts#execution-environment)
|
|
|
|
|
to enable this behavior.
|
|
|
|
|
|
|
|
|
|
### Why?
|
|
|
|
|
|
|
|
|
|
Context isolation allows each the scripts on running in the renderer to make
|
|
|
|
|
changes to its JavaScript environment without worrying about conflicting with
|
|
|
|
|
the scripts in the Electron API or the preload script.
|
|
|
|
|
|
|
|
|
|
While still an experimental Electron feature, context isolation also adds an
|
|
|
|
|
additional layer of security by completely separating any Electron APIs and
|
|
|
|
|
preload scripts from access by the scripts running in the renderer. At the
|
|
|
|
|
same time, preload scripts continue to have access to the `document` and
|
|
|
|
|
`window` object, meaning that you are very likely not reduced in your ability
|
|
|
|
|
to use preload scripts. In other words, you're getting a decent return on a
|
|
|
|
|
likely very small investment.
|
|
|
|
|
|
|
|
|
|
### How?
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// Main process
|
|
|
|
|
const mainWindow = new BrowserWindow({
|
|
|
|
|
webPreferences: {
|
|
|
|
|
contextIsolation: true,
|
|
|
|
|
preload: 'preload.js'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// Preload script
|
|
|
|
|
|
|
|
|
|
// Set a variable in the page before it loads
|
|
|
|
|
webFrame.executeJavaScript('window.foo = "foo";')
|
|
|
|
|
|
|
|
|
|
// The loaded page will not be able to access this, it is only available
|
|
|
|
|
// in this context
|
|
|
|
|
window.bar = 'bar'
|
|
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
|
// Will log out 'undefined' since window.foo is only available in the main
|
|
|
|
|
// context
|
|
|
|
|
console.log(window.foo)
|
|
|
|
|
|
|
|
|
|
// Will log out 'bar' since window.bar is available in this context
|
|
|
|
|
console.log(window.bar)
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
2018-01-29 19:19:43 +00:00
|
|
|
|
|
2018-01-29 19:01:22 +00:00
|
|
|
|
## Handle Session Permission Requests From Remote Content
|
|
|
|
|
|
|
|
|
|
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?
|
|
|
|
|
|
|
|
|
|
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?
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const { session } = require('electron')
|
|
|
|
|
|
|
|
|
|
session
|
|
|
|
|
.fromPartition('some-partition')
|
|
|
|
|
.setPermissionRequestHandler((webContents, permission, callback) => {
|
|
|
|
|
const url = webContents.getURL()
|
|
|
|
|
|
|
|
|
|
if (permission === 'notifications') {
|
|
|
|
|
// Approves the permissions request
|
|
|
|
|
callback(true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!url.startsWith('https://my-website.com')) {
|
|
|
|
|
// Denies the permissions request
|
|
|
|
|
return callback(false)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2018-01-29 19:02:30 +00:00
|
|
|
|
## Define a Content Security Policy
|
|
|
|
|
|
|
|
|
|
A Content Security Policy (CSP) is an additional layer of protection against
|
|
|
|
|
cross-site-scripting attacks (XSS) and data injection attacks. They can be
|
|
|
|
|
enabled by websites and we recommend that any website you load inside Electron
|
|
|
|
|
does so.
|
|
|
|
|
|
|
|
|
|
### Why?
|
|
|
|
|
|
|
|
|
|
CSP allows the server serving content to restrict and control the resources
|
|
|
|
|
Electron will load for that given web page. `https://your-page.com` should have
|
|
|
|
|
be allowed to 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 applications security.
|
|
|
|
|
|
|
|
|
|
### How?
|
|
|
|
|
|
|
|
|
|
Electron respects [the `Content-Security-Policy` HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)
|
|
|
|
|
as well as the respective `<meta>` tag.
|
|
|
|
|
|
|
|
|
|
The following CSP will allow Electron to execute scripts from the current
|
|
|
|
|
website as well as from `apis.mydomain.com`.
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
// Bad
|
|
|
|
|
Content-Security-Policy: '*'
|
|
|
|
|
|
|
|
|
|
// Good
|
|
|
|
|
Content-Security-Policy: script-src 'self' https://apis.mydomain.com
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2018-01-29 19:02:52 +00:00
|
|
|
|
## Override and Disable `eval`
|
|
|
|
|
|
|
|
|
|
`eval()` is a core JavaScript method that allows the execution of JavaScript
|
|
|
|
|
from a string. Disabling it disables your app's ability to evaluate JavaScript
|
|
|
|
|
that is not known in advance.
|
|
|
|
|
|
|
|
|
|
### Why?
|
|
|
|
|
|
|
|
|
|
The `eval()` method has precisely one mission: To evaluate a series of
|
|
|
|
|
characters as JavaScript and execute it. It is a required method whenever you
|
|
|
|
|
need to evaluate code that is known ahead of time. While legitimate use cases
|
|
|
|
|
exist, just like any other code generators, `eval()` is difficult to harden.
|
|
|
|
|
|
|
|
|
|
Generally speaking, it is easier to completely disable `eval()` than to make
|
|
|
|
|
it bulletproof. Thus, if you do not need it, it is a good idea to disable it.
|
|
|
|
|
|
|
|
|
|
### How?
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
window.eval = global.eval = function() {
|
|
|
|
|
throw new Error(`Sorry, this app does not support window.eval().`);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2018-01-29 19:03:06 +00:00
|
|
|
|
## Do Not Set `allowRunningInsecureContent` to `true`
|
|
|
|
|
|
|
|
|
|
By default, Electron will now allow websites loaded over `HTTPS` to load and
|
|
|
|
|
execute scripts, CSS, or plugins from insecure sources (`HTTP`). Setting the
|
|
|
|
|
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?
|
|
|
|
|
|
|
|
|
|
See the section on [only displaying secure content](#only-display-secure-content)
|
|
|
|
|
for more details, but simply put, loading content over `HTTPS` assures the
|
|
|
|
|
authenticity and integrity of the loaded resources while encrypting the traffic
|
|
|
|
|
itself.
|
|
|
|
|
|
|
|
|
|
### How?
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// Bad
|
|
|
|
|
const mainWindow = new BrowserWindow({
|
|
|
|
|
webPreferences: {
|
|
|
|
|
allowRunningInsecureContent: true
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Good
|
|
|
|
|
const mainWindow = new BrowserWindow({})
|
|
|
|
|
```
|
|
|
|
|
|
2018-01-29 19:03:27 +00:00
|
|
|
|
|
|
|
|
|
## Do Not Enable Experimental Features
|
|
|
|
|
|
|
|
|
|
Advanced users of Electron can enable experimental Chromium features using the
|
|
|
|
|
`experimentalFeatures` and `experimentalCanvasFeatures` properties.
|
|
|
|
|
|
|
|
|
|
### Why?
|
|
|
|
|
|
|
|
|
|
Experimental features are, as the name suggests, experimental and have not been
|
|
|
|
|
enabled for all Chromium users. Futhermore, their impact on Electron as a whole
|
|
|
|
|
has likely not been tested.
|
|
|
|
|
|
|
|
|
|
Legitimate use cases exist, but unless you know what you are doing, you should
|
|
|
|
|
not enable this property.
|
|
|
|
|
|
|
|
|
|
### How?
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// Bad
|
|
|
|
|
const mainWindow = new BrowserWindow({
|
|
|
|
|
webPreferences: {
|
|
|
|
|
experimentalFeatures: true
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Good
|
|
|
|
|
const mainWindow = new BrowserWindow({})
|
|
|
|
|
```
|
|
|
|
|
|
2018-01-29 19:03:38 +00:00
|
|
|
|
|
|
|
|
|
## Do Not Use `blinkFeatures`
|
|
|
|
|
Blink is the name of the rendering engine behind Chromium. Similarly to
|
|
|
|
|
`experimentalFeatures`, the `blinkFeatures` property allows developers to
|
|
|
|
|
enable features that have been disabled by default.
|
|
|
|
|
|
|
|
|
|
### 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
|
|
|
|
|
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?
|
|
|
|
|
```js
|
|
|
|
|
// Bad
|
|
|
|
|
const mainWindow = new BrowserWindow({
|
|
|
|
|
webPreferences: {
|
|
|
|
|
blinkFeatures: ['ExecCommandInJavaScript']
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Good
|
|
|
|
|
const mainWindow = new BrowserWindow()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2018-01-29 19:03:51 +00:00
|
|
|
|
## Do Not Disable WebSecurity
|
|
|
|
|
|
|
|
|
|
You may have already guessed that disabling the `webSecurity` property on a
|
|
|
|
|
renderer process (`BrowserView`, `BrowserWindow`, `WebView`) disables crucial
|
|
|
|
|
security features.
|
|
|
|
|
|
|
|
|
|
Legitimate use cases for this property exist in testing cases, but generally
|
|
|
|
|
speaking, `webSecurity` should never be disabled in any production application.
|
|
|
|
|
|
|
|
|
|
### Why?
|
|
|
|
|
|
|
|
|
|
Disabling `webSecurity` will disable the same-origin policy as well as
|
|
|
|
|
implicitly setting the `allowRunningInsecureContent` property to `true`. In
|
|
|
|
|
other words, it allows the execution of insecure code from different domains.
|
|
|
|
|
|
|
|
|
|
### How?
|
|
|
|
|
```js
|
|
|
|
|
// Bad
|
|
|
|
|
const mainWindow = new BrowserWindow({
|
|
|
|
|
webPreferences: {
|
|
|
|
|
webSecurity: false
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Good
|
|
|
|
|
const mainWindow = new BrowserWindow()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```html
|
|
|
|
|
<!-- Bad -->
|
|
|
|
|
<webview disablewebsecurity src="page.html"></webview>
|
|
|
|
|
|
|
|
|
|
<!-- Good -->
|
|
|
|
|
<webview src="page.html"></webview>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2018-01-29 19:04:02 +00:00
|
|
|
|
## Do Not Use `allowpopups`
|
|
|
|
|
|
|
|
|
|
If you are using `WebViews`, you might need the pages and scripts loaded in
|
|
|
|
|
your `<webview>` tag to open new windows. The `allowpopups` attribute enables
|
|
|
|
|
them to create new `BrowserWindows` using the `window.open()` method. By
|
|
|
|
|
default, `WebViews` are not allowed to create new windows.
|
|
|
|
|
|
|
|
|
|
### Why?
|
|
|
|
|
|
|
|
|
|
If you do not need popups, you are better off not allowing the creation of
|
|
|
|
|
new `BrowserWindows` by default. This follows the principle of the minimally
|
|
|
|
|
required access: Websites that you do not know to need popups should not have
|
|
|
|
|
the ability to create new popups.
|
|
|
|
|
|
|
|
|
|
### How?
|
|
|
|
|
|
|
|
|
|
```html
|
|
|
|
|
<!-- Bad -->
|
|
|
|
|
<webview allowpopups src="page.html"></webview>
|
|
|
|
|
|
|
|
|
|
<!-- Good -->
|
|
|
|
|
<webview src="page.html"></webview>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2018-01-29 02:19:49 +00:00
|
|
|
|
## Verify WebView Options Before Creation
|
2018-01-29 19:19:43 +00:00
|
|
|
|
|
2018-01-29 02:19:49 +00:00
|
|
|
|
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
|
|
|
|
|
always create an independent renderer process with its own `webPreferences`.
|
|
|
|
|
|
2018-01-29 19:19:43 +00:00
|
|
|
|
It is a good idea to control the creation of new `WebViews` from the main
|
|
|
|
|
process and to verify that their webPreferences do not disable security
|
|
|
|
|
features.
|
2018-01-29 02:19:49 +00:00
|
|
|
|
|
|
|
|
|
### Why?
|
2018-01-29 19:19:43 +00:00
|
|
|
|
|
2018-01-29 02:19:49 +00:00
|
|
|
|
Since WebViews live in the DOM, they can be created by a script running on your
|
|
|
|
|
website even if Node integration is otherwise disabled.
|
|
|
|
|
|
|
|
|
|
Electron enables developers to disable various security features that control
|
2018-01-29 19:19:43 +00:00
|
|
|
|
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>` tags.
|
2018-01-29 02:19:49 +00:00
|
|
|
|
|
|
|
|
|
### How?
|
2018-01-29 19:19:43 +00:00
|
|
|
|
|
2018-01-29 02:19:49 +00:00
|
|
|
|
Before a `<WebView>` 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
|
|
|
|
|
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
|
|
|
|
|
delete webPreferences.preload
|
|
|
|
|
delete webPreferences.preloadURL
|
|
|
|
|
|
|
|
|
|
// Disable node integration
|
|
|
|
|
webPreferences.nodeIntegration = false
|
|
|
|
|
|
|
|
|
|
// Verify URL being loaded
|
|
|
|
|
if (!params.src.startsWith('https://yourapp.com/')) {
|
|
|
|
|
event.preventDefault()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
2016-05-10 08:41:16 +00:00
|
|
|
|
Again, this list merely minimizes the risk, it does not remove it. If your goal
|
|
|
|
|
is to display a website, a browser will be a more secure option.
|