Browse Source

Add initial documentation for `sandbox` option.

Thiago de Arruda 8 years ago
parent
commit
437f1192d9
2 changed files with 191 additions and 0 deletions
  1. 5 0
      docs/api/browser-window.md
  2. 186 0
      docs/api/sandbox-option.md

+ 5 - 0
docs/api/browser-window.md

@@ -225,6 +225,11 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
       When node integration is turned off, the preload script can reintroduce
       Node global symbols back to the global scope. See example
       [here](process.md#event-loaded).
+    * `sandbox` Boolean (optional) - If set, this will sandbox the renderer
+      associated with the window, making it compatible with chromium 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).
     * `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
       use the `partition` option instead, which accepts a partition string. When

+ 186 - 0
docs/api/sandbox-option.md

@@ -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.