Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add remote module to docs/tutorial/security.md #17480

Merged
merged 3 commits into from Apr 5, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
125 changes: 125 additions & 0 deletions docs/tutorial/security.md
Expand Up @@ -109,6 +109,8 @@ You should at least follow these steps to improve the security of your applicati
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)
14. [Do not use `openExternal` with untrusted content](#14-do-not-use-openexternal-with-untrusted-content)
15. [Disable the `remote` module](#15-disable-the-remote-module)
16. [Filter the `remote` module](#16-filter-the-remote-module)

To automate the detection of misconfigurations and insecure patterns, it is
possible to use
Expand Down Expand Up @@ -709,6 +711,128 @@ const { shell } = require('electron')
shell.openExternal('https://example.com/index.html')
```

## 15) Disable the `remote` module

The `remote` module provides a way for the renderer processes to
access APIs normally only available in the main process. Using it, a
renderer can invoke methods of a main process object without explicitly sending
inter-process messages. If your desktop application does not run untrusted
content, this can be a useful way to have your renderer processes access and
work with modules that are only available to the main process, such as
GUI-related modules (dialogs, menus, etc.).

However, if your app can run untrusted content and even if you
[sandbox][sandbox] your renderer processes accordingly, the `remote` module
makes it easy for malicious code to escape the sandbox and have access to
system resources via the higher privileges of the main process. Therefore,
it should be disabled in such circumstances.

### Why?

`remote` uses an internal IPC channel to communicate with the main process.
"Prototype pollution" attacks can grant malicious code access to the internal
IPC channel, which can then be used to escape the sandbox by mimicking `remote`
IPC messages and getting access to main process modules running with higher
privileges.

Additionally, it's possible for preload scripts to accidentally leak modules to a
sandboxed renderer. Leaking `remote` arms malicious code with a multitude
of main process modules with which to perform an attack.

Disabling the `remote` module eliminates these attack vectors. Enabling
context isolation also prevents the "prototype pollution" attacks from
succeeding.

### How?

```js
// Bad if the renderer can run untrusted content
const mainWindow = new BrowserWindow({})
```

```js
// Good
const mainWindow = new BrowserWindow({
webPreferences: {
enableRemoteModule: false
}
})
```

```html
<!-- Bad if the renderer can run untrusted content -->
<webview src="page.html"></webview>

<!-- Good -->
<webview enableremotemodule="false" src="page.html"></webview>
```

## 16) Filter the `remote` module

If you cannot disable the `remote` module, you should filter the globals,
Node, and Electron modules (so-called built-ins) accessible via `remote`
that your application does not require. This can be done by blocking
certain modules entirely and by replacing others with proxies that
expose only the functionality that your app needs.

### Why?

Due to the system access privileges of the main process, functionality
provided by the main process modules may be dangerous in the hands of
malicious code running in a compromised renderer process. By limiting
the set of accessible modules to the minimum that your app needs and
filtering out the others, you reduce the toolset that malicious code
can use to attack the system.

Note that the safest option is to
[fully disable the remote module](#15-disable-the-remote-module). If
you choose to filter access rather than completely disable the module,
you must be very careful to ensure that no escalation of privilege is
possible through the modules you allow past the filter.

### How?

```js
const readOnlyFsProxy = require(/* ... */) // exposes only file read functionality

const allowedModules = new Set(['crypto'])
const proxiedModules = new Map(['fs', readOnlyFsProxy])
const allowedElectronModules = new Set(['shell'])
const allowedGlobals = new Set()

app.on('remote-require', (event, webContents, moduleName) => {
if (proxiedModules.has(moduleName)) {
event.returnValue = proxiedModules.get(moduleName)
}
if (!allowedModules.has(moduleName)) {
event.preventDefault()
}
})

app.on('remote-get-builtin', (event, webContents, moduleName) => {
if (!allowedElectronModules.has(moduleName)) {
event.preventDefault()
}
})

app.on('remote-get-global', (event, webContents, globalName) => {
if (!allowedGlobals.has(globalName)) {
event.preventDefault()
}
})

app.on('remote-get-current-window', (event, webContents) => {
event.preventDefault()
})

app.on('remote-get-current-web-contents', (event, webContents) => {
event.preventDefault()
})

app.on('remote-get-guest-web-contents', (event, webContents, guestWebContents) => {
event.preventDefault()
})
```

[browser-window]: ../api/browser-window.md
[browser-view]: ../api/browser-view.md
Expand All @@ -717,3 +841,4 @@ shell.openExternal('https://example.com/index.html')
[new-window]: ../api/web-contents.md#event-new-window
[will-navigate]: ../api/web-contents.md#event-will-navigate
[open-external]: ../api/shell.md#shellopenexternalurl-options-callback
[sandbox]: ../api/sandbox-option.md