Merge pull request #8983 from electron/sandbox-initial-documentation
Add initial documentation for `sandbox` option.
This commit is contained in:
commit
ad5a48d255
2 changed files with 193 additions and 1 deletions
|
@ -225,6 +225,13 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||||
When node integration is turned off, the preload script can reintroduce
|
When node integration is turned off, the preload script can reintroduce
|
||||||
Node global symbols back to the global scope. See example
|
Node global symbols back to the global scope. See example
|
||||||
[here](process.md#event-loaded).
|
[here](process.md#event-loaded).
|
||||||
|
* `sandbox` Boolean (optional) - If set, this will sandbox the renderer
|
||||||
|
associated with the window, making it compatible with the Chromium
|
||||||
|
OS-level sandbox and disabling the Node.js engine. This is not the same as
|
||||||
|
the `nodeIntegration` option and the APIs available to the preload script
|
||||||
|
are more limited. Read more about the option [here](sandbox-option.md).
|
||||||
|
**Note:** This option is currently experimental and may change or be
|
||||||
|
removed in future Electron releases.
|
||||||
* `session` [Session](session.md#class-session) (optional) - Sets the session used by the
|
* `session` [Session](session.md#class-session) (optional) - Sets the session used by the
|
||||||
page. Instead of passing the Session object directly, you can also choose to
|
page. Instead of passing the Session object directly, you can also choose to
|
||||||
use the `partition` option instead, which accepts a partition string. When
|
use the `partition` option instead, which accepts a partition string. When
|
||||||
|
@ -282,7 +289,6 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||||
window. Defaults to `false`. See the
|
window. Defaults to `false`. See the
|
||||||
[offscreen rendering tutorial](../tutorial/offscreen-rendering.md) for
|
[offscreen rendering tutorial](../tutorial/offscreen-rendering.md) for
|
||||||
more details.
|
more details.
|
||||||
* `sandbox` Boolean (optional) - Whether to enable Chromium OS-level sandbox.
|
|
||||||
* `contextIsolation` Boolean (optional) - Whether to run Electron APIs and
|
* `contextIsolation` Boolean (optional) - Whether to run Electron APIs and
|
||||||
the specified `preload` script in a separate JavaScript context. Defaults
|
the specified `preload` script in a separate JavaScript context. Defaults
|
||||||
to `false`. The context that the `preload` script runs in will still
|
to `false`. The context that the `preload` script runs in will still
|
||||||
|
|
186
docs/api/sandbox-option.md
Normal file
186
docs/api/sandbox-option.md
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
# `sandbox` Option
|
||||||
|
|
||||||
|
> Create a browser window with renderer that can run inside chromium OS sandbox.
|
||||||
|
|
||||||
|
One of chromium key security features is that all blink rendering/javascript
|
||||||
|
code is confined in a sandbox. This sandbox uses OS-specific features to ensure
|
||||||
|
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.
|
||||||
|
|
||||||
|
Since a major feature in electron is the ability to run node.js in the
|
||||||
|
renderer process(making it easier to develop desktop applications using only web
|
||||||
|
technologies), the sandbox has to disabled by electron. One of the reasons is
|
||||||
|
that most node.js APIs require system access. `require()` for example, is not
|
||||||
|
possible without file system permissions, which are unavailable in a sandboxed
|
||||||
|
environment.
|
||||||
|
|
||||||
|
Usually this is not a problem for desktop applications since the code is always
|
||||||
|
trusted, but it makes electron less secure than chromium for displaying
|
||||||
|
untrusted web content. For applications that require more security, the
|
||||||
|
`sandbox` flag will force electron to spawn a classic chromium renderer that is
|
||||||
|
compatible with the sandbox.
|
||||||
|
|
||||||
|
A sandboxed renderer doesn't have a node.js environment running and doesn't
|
||||||
|
expose javascript APIs to client code. The only exception is the preload script,
|
||||||
|
which has access to a subset of electron renderer API.
|
||||||
|
|
||||||
|
Another difference is that sandboxed renderers don't modify any of the default
|
||||||
|
javascript APIs. Consequently, some APIs such as `window.open` will work as they
|
||||||
|
do in chromium(no `BrowserWindowProxy`).
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Create a sandboxed window, simply pass `sandbox: true` to `webPreferences`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let win
|
||||||
|
app.on('ready', () => {
|
||||||
|
win = new BrowserWindow({
|
||||||
|
webPreferences: {
|
||||||
|
sandbox: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
w.loadURL('http://google.com')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
This alone won't enable the OS-enforced sandbox. To use it, the
|
||||||
|
`--enable-sandbox` command-line argument must be passed to electron, which will
|
||||||
|
force `sandbox: true` to all BrowserWindow instances.
|
||||||
|
|
||||||
|
```js
|
||||||
|
let win
|
||||||
|
app.on('ready', () => {
|
||||||
|
// no need to pass `sandbox: true` since `--enable-sandbox` was enabled.
|
||||||
|
win = new BrowserWindow()
|
||||||
|
w.loadURL('http://google.com')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that it is not enough to call
|
||||||
|
`app.commandLine.appendSwitch('--enable-sandbox')`, as electron/node startup
|
||||||
|
code runs after it is possible to make changes to chromium sandbox settings. The
|
||||||
|
switch must be passed to electron command-line:
|
||||||
|
|
||||||
|
```
|
||||||
|
electron --enable-sandbox app.js
|
||||||
|
```
|
||||||
|
|
||||||
|
It is not possible to have the OS sandbox active only for some renderers, if
|
||||||
|
`--enable-sandbox` is enabled, normal electron windows cannot be created.
|
||||||
|
|
||||||
|
If you need to mix sandboxed and non-sandboxed renderers in one application,
|
||||||
|
simply omit the `--enable-sandbox` argument. Without this argument, windows
|
||||||
|
created with `sandbox: true` will still have node.js disabled and communicate
|
||||||
|
only via IPC, which by itself is already a gain from security POV.
|
||||||
|
|
||||||
|
## Preload
|
||||||
|
|
||||||
|
An app can make customizations to sandboxed renderers using a preload script.
|
||||||
|
Here's an example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let win
|
||||||
|
app.on('ready', () => {
|
||||||
|
win = new BrowserWindow({
|
||||||
|
webPreferences: {
|
||||||
|
sandbox: true,
|
||||||
|
preload: 'preload.js'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
w.loadURL('http://google.com')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
and preload.js:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// This file is loaded whenever a javascript context is created. It runs in a
|
||||||
|
// private scope that can access a subset of electron renderer APIs. We must be
|
||||||
|
// careful to not leak any objects into the global scope!
|
||||||
|
const fs = require('fs')
|
||||||
|
const {ipcRenderer} = require('electron')
|
||||||
|
|
||||||
|
// 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:
|
||||||
|
|
||||||
|
- Even though the sandboxed renderer doesn't have node.js running, it still has
|
||||||
|
access to a limited node-like environment:`Buffer`, `process`, `setImmediate`
|
||||||
|
and `require` are available.
|
||||||
|
- The preload can indirectly access all APIs from the main process through the
|
||||||
|
`remote` and `ipcRenderer` modules. This is how `fs`(used above) and other
|
||||||
|
modules are implemented: They are proxies to remote counterparts in the main
|
||||||
|
process.
|
||||||
|
- The preload must be contained in a single script, but it is possible to have
|
||||||
|
complex preload code composed with multiple modules by using a tool like
|
||||||
|
browserify, as explained below. In fact, browserify is already used by
|
||||||
|
electron to provide a node-like environment to the preload script.
|
||||||
|
|
||||||
|
To create a browserify bundle and use it as a preload script, something like
|
||||||
|
the following should be used:
|
||||||
|
|
||||||
|
browserify preload/index.js \
|
||||||
|
-x electron \
|
||||||
|
-x fs \
|
||||||
|
--insert-global-vars=__filename,__dirname -o preload.js
|
||||||
|
|
||||||
|
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
|
||||||
|
for it. `--insert-global-vars` will ensure that `process`,`Buffer` and
|
||||||
|
`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:
|
||||||
|
|
||||||
|
- `child_process`
|
||||||
|
- `electron`(crashReporter, remote and ipcRenderer)
|
||||||
|
- `fs`
|
||||||
|
- `os`
|
||||||
|
- `timers`
|
||||||
|
- `url`
|
||||||
|
|
||||||
|
More may be added as needed to expose more electron APIs in the sandbox, but any
|
||||||
|
module in the main process can already be used through
|
||||||
|
`electron.remote.require`.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Please use the `sandbox` option with care, as it still is an experimental
|
||||||
|
feature. We are still not aware of the security implications of exposing some
|
||||||
|
electron renderer APIs to the preload script, but here are some things to
|
||||||
|
consider before rendering untrusted content:
|
||||||
|
|
||||||
|
- A preload script can accidentaly leak privileged APIs to untrusted code.
|
||||||
|
- 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`
|
||||||
|
module.
|
||||||
|
|
||||||
|
Since renderering untrusted content in electron is still uncharted territory,
|
||||||
|
the APIs exposed to the sandbox preload script should be considered more
|
||||||
|
unstable than the rest of electron APIs, and may have breaking changes to fix
|
||||||
|
security issues.
|
||||||
|
|
||||||
|
One planned enhancement that should greatly increase security is to block IPC
|
||||||
|
messages from sandboxed renderers by default, allowing the main process
|
||||||
|
explicitly define a set of messages the renderer is allowed to send.
|
Loading…
Reference in a new issue