Browse Source

docs: rework sandbox guide (#29104)

Co-authored-by: Samuel Attard <[email protected]>
Co-authored-by: Biru Mohanathas <[email protected]>
Co-authored-by: Jeremy Rose <[email protected]>

Co-authored-by: Samuel Attard <[email protected]>
Co-authored-by: Biru Mohanathas <[email protected]>
Co-authored-by: Jeremy Rose <[email protected]>
Erick Zhao 3 years ago
parent
commit
120a8acfd0

+ 1 - 0
docs/README.md

@@ -59,6 +59,7 @@ an issue:
   * [Using Native Node.js Modules](tutorial/using-native-node-modules.md)
   * [Performance Strategies](tutorial/performance.md)
   * [Security Strategies](tutorial/security.md)
+  * [Process Sandboxing](tutorial/sandbox.md)
 * [Accessibility](tutorial/accessibility.md)
   * [Manually Enabling Accessibility Features](tutorial/accessibility.md#manually-enabling-accessibility-features)
 * [Testing and Debugging](tutorial/application-debugging.md)

+ 1 - 1
docs/api/browser-window.md

@@ -272,7 +272,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
       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).
+      are more limited. Read more about the option [here](../tutorial/sandbox.md).
     * `enableRemoteModule` Boolean (optional) - Whether to enable the [`remote`](remote.md) module.
       Default is `false`.
     * `session` [Session](session.md#class-session) (optional) - Sets the session used by the

+ 2 - 1
docs/api/command-line-switches.md

@@ -142,7 +142,8 @@ proxy server flags that are passed.
 
 ### --no-sandbox
 
-Disables Chromium sandbox, which is now enabled by default.
+Disables the Chromium [sandbox](https://www.chromium.org/developers/design-documents/sandbox).
+Forces renderer process and Chromium helper processes to run un-sandboxed.
 Should only be used for testing.
 
 ### --proxy-bypass-list=`hosts`

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

@@ -1,182 +0,0 @@
-# `sandbox` Option
-
-> Create a browser window with a sandboxed renderer. With this
-option enabled, the renderer must communicate via IPC to the main process in order to access node APIs.
-
-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
-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 web
-technologies), the sandbox is disabled by electron. This is because
-most Node.js APIs require system access. `require()` for example, is not
-possible without file system permissions, which are not available 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 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.
-
-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 (i.e. they do not return a [`BrowserWindowProxy`](browser-window-proxy.md)).
-
-## Example
-
-To create a sandboxed window, pass `sandbox: true` to `webPreferences`:
-
-```js
-let win
-app.whenReady().then(() => {
-  win = new BrowserWindow({
-    webPreferences: {
-      sandbox: true
-    }
-  })
-  win.loadURL('http://google.com')
-})
-```
-
-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,
-within this new window `window.open` follows the native behavior (by default Electron creates a [`BrowserWindow`](browser-window.md)
-and returns a proxy to this via `window.open`).
-
-[`app.enableSandbox`](app.md#appenablesandbox) can be used to force `sandbox: true` for all `BrowserWindow` instances.
-
-```js
-let win
-app.enableSandbox()
-app.whenReady().then(() => {
-  // no need to pass `sandbox: true` since `app.enableSandbox()` was called.
-  win = new BrowserWindow()
-  win.loadURL('http://google.com')
-})
-```
-
-## Preload
-
-An app can make customizations to sandboxed renderers using a preload script.
-Here's an example:
-
-```js
-let win
-app.whenReady().then(() => {
-  win = new BrowserWindow({
-    webPreferences: {
-      sandbox: true,
-      preload: path.join(app.getAppPath(), 'preload.js')
-    }
-  })
-  win.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. Without
-// contextIsolation enabled, it's possible to accidentally leak privileged
-// globals like ipcRenderer to web content.
-const { ipcRenderer } = require('electron')
-
-const defaultWindowOpen = window.open
-
-window.open = function customWindowOpen (url, ...args) {
-  ipcRenderer.send('report-window-open', location.origin, url, args)
-  return defaultWindowOpen(url + '?from_electron=1', ...args)
-}
-```
-
-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`,
-  `clearImmediate` and `require` are available.
-- The preload script 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
-  webpack or browserify. An example of using browserify is below.
-
-To create a browserify bundle and use it as a preload script, something like
-the following should be used:
-
-```sh
-  browserify preload/index.js \
-    -x electron \
-    --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:
-
-- `electron`
-  - `crashReporter`
-  - `desktopCapturer`
-  - `ipcRenderer`
-  - `nativeImage`
-  - `webFrame`
-- `events`
-- `timers`
-- `url`
-
-More may be added as needed to expose more Electron APIs in the sandbox.
-
-## Rendering untrusted content
-
-Rendering untrusted content in Electron is still somewhat uncharted territory,
-though some apps are finding success (e.g. Beaker Browser). Our goal is to get
-as close to Chrome as we can in terms of the security of sandboxed content, but
-ultimately we will always be behind due to a few fundamental issues:
-
-1. We do not have the dedicated resources or expertise that Chromium has to
-   apply to the security of its product. We do our best to make use of what we
-   have, to inherit everything we can from Chromium, and to respond quickly to
-   security issues, but Electron cannot be as secure as Chromium without the
-   resources that Chromium is able to dedicate.
-2. Some security features in Chrome (such as Safe Browsing and Certificate
-   Transparency) require a centralized authority and dedicated servers, both of
-   which run counter to the goals of the Electron project. As such, we disable
-   those features in Electron, at the cost of the associated security they
-   would otherwise bring.
-3. There is only one Chromium, whereas there are many thousands of apps built
-   on Electron, all of which behave slightly differently. Accounting for those
-   differences can yield a huge possibility space, and make it challenging to
-   ensure the security of the platform in unusual use cases.
-4. We can't push security updates to users directly, so we rely on app vendors
-   to upgrade the version of Electron underlying their app in order for
-   security updates to reach users.
-
-Here are some things to consider before rendering untrusted content:
-
-- 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.
-- Some bug in the V8 engine may allow malicious code to access the renderer
-  preload APIs, effectively granting full access to the system through the
-  `remote` 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).
-- While we make our best effort to backport Chromium security fixes to older
-  versions of Electron, we do not make a guarantee that every fix will be
-  backported. Your best chance at staying secure is to be on the latest stable
-  version of Electron.

+ 169 - 0
docs/tutorial/sandbox.md

@@ -0,0 +1,169 @@
+# Process Sandboxing
+
+One key security feature in Chromium is that processes can be executed within a sandbox.
+The sandbox limits the harm that malicious code can cause by limiting access to most
+system resources — sandboxed processes can only freely use CPU cycles and memory.
+In order to perform operations requiring additional privilege, sandboxed processes
+use dedicated communication channels to delegate tasks to more privileged processes.
+
+In Chromium, sandboxing is applied to most processes other than the main process.
+This includes renderer processes, as well as utility processes such as the audio service,
+the GPU service and the network service.
+
+See Chromium's [Sandbox design document][sandbox] for more information.
+
+## Electron's sandboxing policies
+
+Electron comes with a mixed sandbox environment, meaning sandboxed processes can run
+alongside privileged ones. By default, renderer processes are not sandboxed, but
+utility processes are. Note that as in Chromium, the main (browser) process is
+privileged and cannot be sandboxed.
+
+Historically, this mixed sandbox approach was established because having Node.js available
+in the renderer is an extremely powerful tool for app developers. Unfortunately, this
+feature is also an equally massive security vulnerability.
+
+Theoretically, unsandboxed renderers are not a problem for desktop applications that
+only display trusted code, but they make Electron less secure than Chromium for
+displaying untrusted web content. However, even purportedly trusted code may be
+dangerous — there are countless attack vectors that malicious actors can use, from
+cross-site scripting to content injection to man-in-the-middle attacks on remotely loaded
+websites, just to name a few. For this reason, we recommend enabling renderer sandboxing
+for the vast majority of cases under an abundance of caution.
+
+<!--TODO: update this guide when #28466 is either solved or closed -->
+Note that there is an active discussion in the issue tracker to enable renderer sandboxing
+by default. See [#28466][issue-28466]) for details.
+
+## Sandbox behaviour in Electron
+
+Sandboxed processes in Electron behave _mostly_ in the same way as Chromium's do, but
+Electron has a few additional concepts to consider because it interfaces with Node.js.
+
+### Renderer processes
+
+When renderer processes in Electron are sandboxed, they behave in the same way as a
+regular Chrome renderer would. A sandboxed renderer won't have a Node.js
+environment initialized.
+
+<!-- TODO(erickzhao): when we have a solid guide for IPC, link it here -->
+Therefore, when the sandbox is enabled, renderer processes can only perform privileged
+tasks (such as interacting with the filesystem, making changes to the system, or spawning
+subprocesses) by delegating these tasks to the main process via inter-process
+communication (IPC).
+
+### Preload scripts
+
+In order to allow renderer processes to communicate with the main process, preload
+scripts attached to sandboxed renderers will still have a polyfilled subset of Node.js
+APIs available. A `require` function similar to Node's `require` module is exposed,
+but can only import a subset of Electron and Node's built-in modules:
+
+* `electron` (only renderer process modules)
+* [`events`](https://nodejs.org/api/events.html)
+* [`timers`](https://nodejs.org/api/timers.html)
+* [`url`](https://nodejs.org/api/url.html)
+
+In addition, the preload script also polyfills certain Node.js primitives as globals:
+
+* [`Buffer`](https://nodejs.org/api/Buffer.html)
+* [`process`](../api/process.md)
+* [`clearImmediate`](https://nodejs.org/api/timers.html#timers_clearimmediate_immediate)
+* [`setImmediate`](https://nodejs.org/api/timers.html#timers_setimmediate_callback_args)
+
+Because the `require` function is a polyfill with limited functionality, you will not be
+able to use [CommonJS modules][commonjs] to separate your preload script into multiple
+files. If you need to split your preload code, use a bundler such as [webpack][webpack]
+or [Parcel][parcel].
+
+Note that because the environment presented to the `preload` script is substantially
+more privileged than that of a sandboxed renderer, it is still possible to leak
+privileged APIs to untrusted code running in the renderer process unless
+[`contextIsolation`][contextIsolation] is enabled.
+
+## Configuring the sandbox
+
+### Enabling the sandbox for a single process
+
+In Electron, renderer sandboxing can be enabled on a per-process basis with
+the `sandbox: true` preference in the [`BrowserWindow`][browser-window] constructor.
+
+```js
+// main.js
+app.whenReady().then(() => {
+  const win = new BrowserWindow({
+    webPreferences: {
+      sandbox: true
+    }
+  })
+  win.loadURL('https://google.com')
+})
+```
+
+### Enabling the sandbox globally
+
+If you want to force sandboxing for all renderers, you can also use the
+[`app.enableSandbox`][enable-sandbox] API. Note that this API has to be called before the
+app's `ready` event.
+
+```js
+// main.js
+app.enableSandbox()
+app.whenReady().then(() => {
+  // no need to pass `sandbox: true` since `app.enableSandbox()` was called.
+  const win = new BrowserWindow()
+  win.loadURL('https://google.com')
+})
+```
+
+### Disabling Chromium's sandbox (testing only)
+
+You can also disable Chromium's sandbox entirely with the [`--no-sandbox`][no-sandbox]
+CLI flag, which will disable the sandbox for all processes (including utility processes).
+We highly recommend that you only use this flag for testing purposes, and **never**
+in production.
+
+Note that the `sandbox: true` option will still disable the renderer's Node.js
+environment.
+
+## A note on rendering untrusted content
+
+Rendering untrusted content in Electron is still somewhat uncharted territory,
+though some apps are finding success (e.g. [Beaker Browser][beaker]).
+Our goal is to get as close to Chrome as we can in terms of the security of
+sandboxed content, but ultimately we will always be behind due to a few fundamental
+issues:
+
+1. We do not have the dedicated resources or expertise that Chromium has to
+   apply to the security of its product. We do our best to make use of what we
+   have, to inherit everything we can from Chromium, and to respond quickly to
+   security issues, but Electron cannot be as secure as Chromium without the
+   resources that Chromium is able to dedicate.
+2. Some security features in Chrome (such as Safe Browsing and Certificate
+   Transparency) require a centralized authority and dedicated servers, both of
+   which run counter to the goals of the Electron project. As such, we disable
+   those features in Electron, at the cost of the associated security they
+   would otherwise bring.
+3. There is only one Chromium, whereas there are many thousands of apps built
+   on Electron, all of which behave slightly differently. Accounting for those
+   differences can yield a huge possibility space, and make it challenging to
+   ensure the security of the platform in unusual use cases.
+4. We can't push security updates to users directly, so we rely on app vendors
+   to upgrade the version of Electron underlying their app in order for
+   security updates to reach users.
+
+While we make our best effort to backport Chromium security fixes to older
+versions of Electron, we do not make a guarantee that every fix will be
+backported. Your best chance at staying secure is to be on the latest stable
+version of Electron.
+
+[sandbox]: https://chromium.googlesource.com/chromium/src/+/master/docs/design/sandbox.md
+[issue-28466]: https://github.com/electron/electron/issues/28466
+[browser-window]: ../api/browser-window.md
+[enable-sandbox]: ../api/app.md#appenablesandbox
+[no-sandbox]: ../api/command-line-switches.md#--no-sandbox
+[commonjs]: https://nodejs.org/api/modules.html#modules_modules_commonjs_modules
+[webpack]: https://webpack.js.org/
+[parcel]: https://parceljs.org/
+[context-isolation]: ./context-isolation.md
+[beaker]: https://github.com/beakerbrowser/beaker

+ 1 - 1
docs/tutorial/security.md

@@ -692,5 +692,5 @@ which potential security issues are not as widely known.
 [window-open-handler]: ../api/web-contents.md#contentssetwindowopenhandlerhandler
 [will-navigate]: ../api/web-contents.md#event-will-navigate
 [open-external]: ../api/shell.md#shellopenexternalurl-options
-[sandbox]: ../api/sandbox-option.md
+[sandbox]: ../tutorial/sandbox.md
 [responsible-disclosure]: https://en.wikipedia.org/wiki/Responsible_disclosure

+ 0 - 1
filenames.auto.gni

@@ -43,7 +43,6 @@ auto_filenames = {
     "docs/api/process.md",
     "docs/api/protocol.md",
     "docs/api/remote.md",
-    "docs/api/sandbox-option.md",
     "docs/api/screen.md",
     "docs/api/service-workers.md",
     "docs/api/session.md",