docs: add remote module to docs/tutorial/security.md (#17480)
This commit is contained in:
parent
2b9bd0f56f
commit
235eea6669
1 changed files with 125 additions and 0 deletions
|
@ -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)
|
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)
|
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
|
To automate the detection of misconfigurations and insecure patterns, it is
|
||||||
possible to use
|
possible to use
|
||||||
|
@ -709,6 +711,128 @@ const { shell } = require('electron')
|
||||||
shell.openExternal('https://example.com/index.html')
|
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-window]: ../api/browser-window.md
|
||||||
[browser-view]: ../api/browser-view.md
|
[browser-view]: ../api/browser-view.md
|
||||||
|
@ -717,3 +841,4 @@ shell.openExternal('https://example.com/index.html')
|
||||||
[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
|
[open-external]: ../api/shell.md#shellopenexternalurl-options-callback
|
||||||
|
[sandbox]: ../api/sandbox-option.md
|
||||||
|
|
Loading…
Reference in a new issue