2017-03-09 12:23:03 +00:00
|
|
|
# `sandbox` Option
|
|
|
|
|
2019-03-21 13:41:53 +00:00
|
|
|
> Create a browser window with a sandboxed renderer. With this
|
2017-03-29 13:27:01 +00:00
|
|
|
option enabled, the renderer must communicate via IPC to the main process in order to access node APIs.
|
2017-03-09 12:23:03 +00:00
|
|
|
|
2017-03-29 13:27:01 +00:00
|
|
|
One of the key security features of Chromium is that all blink rendering/JavaScript
|
|
|
|
code is executed within a sandbox. This sandbox uses OS-specific features to ensure
|
2017-03-09 12:23:03 +00:00
|
|
|
that exploits in the renderer process cannot harm the system.
|
|
|
|
|
|
|
|
In other words, when the sandbox is enabled, the renderers can only make changes
|
|
|
|
to the system by delegating tasks to the main process via IPC.
|
|
|
|
[Here's](https://www.chromium.org/developers/design-documents/sandbox) more
|
|
|
|
information about the sandbox.
|
|
|
|
|
2018-07-20 17:58:19 +00:00
|
|
|
Since a major feature in Electron is the ability to run Node.js in the
|
2017-03-29 13:27:01 +00:00
|
|
|
renderer process (making it easier to develop desktop applications using web
|
|
|
|
technologies), the sandbox is disabled by electron. This is because
|
2018-07-20 17:58:19 +00:00
|
|
|
most Node.js APIs require system access. `require()` for example, is not
|
2017-03-29 13:27:01 +00:00
|
|
|
possible without file system permissions, which are not available in a sandboxed
|
2017-03-09 12:23:03 +00:00
|
|
|
environment.
|
|
|
|
|
|
|
|
Usually this is not a problem for desktop applications since the code is always
|
2018-07-20 17:58:19 +00:00
|
|
|
trusted, but it makes Electron less secure than Chromium for displaying
|
2017-03-09 12:23:03 +00:00
|
|
|
untrusted web content. For applications that require more security, the
|
2018-07-20 17:58:19 +00:00
|
|
|
`sandbox` flag will force Electron to spawn a classic Chromium renderer that is
|
2017-03-09 12:23:03 +00:00
|
|
|
compatible with the sandbox.
|
|
|
|
|
2018-07-20 17:58:19 +00:00
|
|
|
A sandboxed renderer doesn't have a Node.js environment running and doesn't
|
|
|
|
expose Node.js JavaScript APIs to client code. The only exception is the preload script,
|
|
|
|
which has access to a subset of the Electron renderer API.
|
2017-03-09 12:23:03 +00:00
|
|
|
|
|
|
|
Another difference is that sandboxed renderers don't modify any of the default
|
2017-03-29 13:27:01 +00:00
|
|
|
JavaScript APIs. Consequently, some APIs such as `window.open` will work as they
|
2018-07-20 17:58:19 +00:00
|
|
|
do in Chromium (i.e. they do not return a [`BrowserWindowProxy`](browser-window-proxy.md)).
|
2017-03-09 12:23:03 +00:00
|
|
|
|
|
|
|
## Example
|
|
|
|
|
2018-05-08 05:16:09 +00:00
|
|
|
To create a sandboxed window, pass `sandbox: true` to `webPreferences`:
|
2017-03-09 12:23:03 +00:00
|
|
|
|
|
|
|
```js
|
|
|
|
let win
|
2020-02-03 22:43:22 +00:00
|
|
|
app.whenReady().then(() => {
|
2017-03-09 12:23:03 +00:00
|
|
|
win = new BrowserWindow({
|
|
|
|
webPreferences: {
|
|
|
|
sandbox: true
|
|
|
|
}
|
|
|
|
})
|
2017-09-13 20:53:30 +00:00
|
|
|
win.loadURL('http://google.com')
|
2017-03-09 12:23:03 +00:00
|
|
|
})
|
|
|
|
```
|
|
|
|
|
2018-07-20 17:58:19 +00:00
|
|
|
In the above code the [`BrowserWindow`](browser-window.md) that was created has Node.js disabled and can communicate
|
|
|
|
only via IPC. The use of this option stops Electron from creating a Node.js runtime in the renderer. Also,
|
2020-03-31 04:06:25 +00:00
|
|
|
within this new window `window.open` follows the native behavior (by default Electron creates a [`BrowserWindow`](browser-window.md)
|
2017-03-29 13:27:01 +00:00
|
|
|
and returns a proxy to this via `window.open`).
|
|
|
|
|
2019-03-21 13:41:53 +00:00
|
|
|
[`app.enableSandbox`](app.md#appenablesandbox-experimental) can be used to force `sandbox: true` for all `BrowserWindow` instances.
|
2017-03-29 13:27:01 +00:00
|
|
|
|
2017-03-09 12:23:03 +00:00
|
|
|
```js
|
|
|
|
let win
|
2019-03-21 13:41:53 +00:00
|
|
|
app.enableSandbox()
|
2020-02-03 22:43:22 +00:00
|
|
|
app.whenReady().then(() => {
|
2019-03-21 13:41:53 +00:00
|
|
|
// no need to pass `sandbox: true` since `app.enableSandbox()` was called.
|
2017-03-09 12:23:03 +00:00
|
|
|
win = new BrowserWindow()
|
2017-09-13 20:53:30 +00:00
|
|
|
win.loadURL('http://google.com')
|
2017-03-09 12:23:03 +00:00
|
|
|
})
|
|
|
|
```
|
|
|
|
|
|
|
|
## Preload
|
|
|
|
|
|
|
|
An app can make customizations to sandboxed renderers using a preload script.
|
|
|
|
Here's an example:
|
|
|
|
|
|
|
|
```js
|
|
|
|
let win
|
2020-02-03 22:43:22 +00:00
|
|
|
app.whenReady().then(() => {
|
2017-03-09 12:23:03 +00:00
|
|
|
win = new BrowserWindow({
|
|
|
|
webPreferences: {
|
|
|
|
sandbox: true,
|
2019-03-28 10:38:51 +00:00
|
|
|
preload: path.join(app.getAppPath(), 'preload.js')
|
2017-03-09 12:23:03 +00:00
|
|
|
}
|
|
|
|
})
|
2017-09-13 20:53:30 +00:00
|
|
|
win.loadURL('http://google.com')
|
2017-03-09 12:23:03 +00:00
|
|
|
})
|
|
|
|
```
|
|
|
|
|
|
|
|
and preload.js:
|
|
|
|
|
|
|
|
```js
|
|
|
|
// This file is loaded whenever a javascript context is created. It runs in a
|
2018-07-20 17:58:19 +00:00
|
|
|
// private scope that can access a subset of Electron renderer APIs. We must be
|
2017-03-09 12:23:03 +00:00
|
|
|
// careful to not leak any objects into the global scope!
|
2019-03-21 13:41:53 +00:00
|
|
|
const { ipcRenderer, remote } = require('electron')
|
|
|
|
const fs = remote.require('fs')
|
2017-03-09 12:23:03 +00:00
|
|
|
|
|
|
|
// read a configuration file using the `fs` module
|
|
|
|
const buf = fs.readFileSync('allowed-popup-urls.json')
|
|
|
|
const allowedUrls = JSON.parse(buf.toString('utf8'))
|
|
|
|
|
|
|
|
const defaultWindowOpen = window.open
|
|
|
|
|
|
|
|
function customWindowOpen (url, ...args) {
|
|
|
|
if (allowedUrls.indexOf(url) === -1) {
|
|
|
|
ipcRenderer.sendSync('blocked-popup-notification', location.origin, url)
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
return defaultWindowOpen(url, ...args)
|
|
|
|
}
|
|
|
|
|
|
|
|
window.open = customWindowOpen
|
|
|
|
```
|
|
|
|
|
|
|
|
Important things to notice in the preload script:
|
|
|
|
|
2018-07-20 17:58:19 +00:00
|
|
|
- Even though the sandboxed renderer doesn't have Node.js running, it still has
|
2019-05-20 15:34:57 +00:00
|
|
|
access to a limited node-like environment: `Buffer`, `process`, `setImmediate`,
|
|
|
|
`clearImmediate` and `require` are available.
|
2017-03-29 13:27:01 +00:00
|
|
|
- The preload script can indirectly access all APIs from the main process through the
|
2019-03-21 13:41:53 +00:00
|
|
|
`remote` and `ipcRenderer` modules.
|
2017-03-29 13:27:01 +00:00
|
|
|
- The preload script must be contained in a single script, but it is possible to have
|
2017-03-09 12:23:03 +00:00
|
|
|
complex preload code composed with multiple modules by using a tool like
|
2019-06-02 20:03:03 +00:00
|
|
|
webpack or browserify. An example of using browserify is below.
|
2017-03-09 12:23:03 +00:00
|
|
|
|
|
|
|
To create a browserify bundle and use it as a preload script, something like
|
|
|
|
the following should be used:
|
|
|
|
|
2017-11-20 06:18:24 +00:00
|
|
|
```sh
|
|
|
|
browserify preload/index.js \
|
|
|
|
-x electron \
|
|
|
|
--insert-global-vars=__filename,__dirname -o preload.js
|
|
|
|
```
|
2017-03-09 12:23:03 +00:00
|
|
|
|
|
|
|
The `-x` flag should be used with any required module that is already exposed in
|
|
|
|
the preload scope, and tells browserify to use the enclosing `require` function
|
2017-03-29 13:27:01 +00:00
|
|
|
for it. `--insert-global-vars` will ensure that `process`, `Buffer` and
|
2017-03-09 12:23:03 +00:00
|
|
|
`setImmediate` are also taken from the enclosing scope(normally browserify
|
|
|
|
injects code for those).
|
|
|
|
|
|
|
|
Currently the `require` function provided in the preload scope exposes the
|
|
|
|
following modules:
|
|
|
|
|
2017-08-09 00:10:44 +00:00
|
|
|
- `electron`
|
|
|
|
- `crashReporter`
|
2019-03-21 13:41:53 +00:00
|
|
|
- `desktopCapturer`
|
2017-08-09 00:10:44 +00:00
|
|
|
- `ipcRenderer`
|
2019-03-21 13:41:53 +00:00
|
|
|
- `nativeImage`
|
|
|
|
- `remote`
|
2017-08-09 00:10:44 +00:00
|
|
|
- `webFrame`
|
2019-03-21 13:41:53 +00:00
|
|
|
- `events`
|
2017-03-09 12:23:03 +00:00
|
|
|
- `timers`
|
|
|
|
- `url`
|
|
|
|
|
2018-07-20 17:58:19 +00:00
|
|
|
More may be added as needed to expose more Electron APIs in the sandbox, but any
|
2017-03-09 12:23:03 +00:00
|
|
|
module in the main process can already be used through
|
|
|
|
`electron.remote.require`.
|
|
|
|
|
|
|
|
## Status
|
|
|
|
|
2017-03-29 13:27:01 +00:00
|
|
|
Please use the `sandbox` option with care, as it is still an experimental
|
2017-03-09 12:23:03 +00:00
|
|
|
feature. We are still not aware of the security implications of exposing some
|
2018-07-20 17:58:19 +00:00
|
|
|
Electron renderer APIs to the preload script, but here are some things to
|
2017-03-09 12:23:03 +00:00
|
|
|
consider before rendering untrusted content:
|
|
|
|
|
2019-05-20 15:34:57 +00:00
|
|
|
- A preload script can accidentally leak privileged APIs to untrusted code,
|
|
|
|
unless [`contextIsolation`](../tutorial/security.md#3-enable-context-isolation-for-remote-content)
|
|
|
|
is also enabled.
|
2017-03-09 12:23:03 +00:00
|
|
|
- Some bug in V8 engine may allow malicious code to access the renderer preload
|
|
|
|
APIs, effectively granting full access to the system through the `remote`
|
2019-05-20 15:34:57 +00:00
|
|
|
module. Therefore, it is highly recommended to
|
|
|
|
[disable the `remote` module](../tutorial/security.md#15-disable-the-remote-module).
|
|
|
|
If disabling is not feasible, you should selectively
|
|
|
|
[filter the `remote` module](../tutorial/security.md#16-filter-the-remote-module).
|
2017-03-09 12:23:03 +00:00
|
|
|
|
2018-07-20 17:58:19 +00:00
|
|
|
Since rendering untrusted content in Electron is still uncharted territory,
|
2017-03-09 12:23:03 +00:00
|
|
|
the APIs exposed to the sandbox preload script should be considered more
|
2018-07-20 17:58:19 +00:00
|
|
|
unstable than the rest of Electron APIs, and may have breaking changes to fix
|
2017-03-09 12:23:03 +00:00
|
|
|
security issues.
|