sandbox 沙盒选项

Create a browser window with a sandboxed renderer. 在该模式可用情况下,渲染器为了使用node APIs必须通过IPC与主进程通讯。

Chromium主要的安全特征之一便是所有的blink渲染或者JavaScript代码都在sandbox内运行。 该sandbox使用OS特定特征来保障运行在渲染器内的进程不会损害系统。

也就是说,在sandbox模式下,渲染器只能通过IPC委派任务给主进程来对操作系统进行更改。 下述是有关sandbox更多的信息。

Electron的一个主要特性就是能在渲染进程中运行Node.js(使用web技术能让我们更加便捷的构建一个桌面应用),但是在渲染进程中沙箱是不可用的。 这是因为大多数Node.js 的API都需要系统权限。 比如 ,没有文件系统权限的情况下require()是不可用的,而该文件系统权限在沙箱环境下是不可用的。

通常,对于桌面应用来说这些都不是问题,因为应用的代码都是可信的;但是显示一些不是那么受信任的网站会使得Electron相比Chromium而言安全性下降。 因为应用程序需要更多的安全性,sandbox 标记将使electron产生一个与沙箱兼容的经典chromium渲染器。

一个沙箱环境下的渲染器没有node.js运行环境,并且不会将Node.js 的 JavaScript APIs 暴露给客户端代码。 唯一的例外是预加载脚本, 它可以访问electron渲染器 API 的一个子集(subset)。

另一个区别是沙箱渲染器不修改任何默认的 JavaScript API。 因此, 某些 api ,(比如 window.open)将像在chromium中一样工作 (即它们不返回 BrowserWindowProxy `)。

示例

创建沙盒窗口, 只需将 sandbox: true 传递到 webPreferences `:

  1. let win
  2. app.whenReady().then(() => {
  3. win = new BrowserWindow({
  4. webPreferences: {
  5. sandbox: true
  6. }
  7. })
  8. win.loadURL('http://google.com')
  9. })

以上代码中被创建的BrowserWindow禁用了node.js,并且只能使用IPC通信。 这个选项的设置阻止electron在渲染器中创建一个node.js运行环境。 Also, within this new window window.open follows the native behavior (by default Electron creates a BrowserWindow and returns a proxy to this via window.open).

app.enableSandbox can be used to force sandbox: true for all BrowserWindow instances.

  1. let win
  2. app.enableSandbox()
  3. app.whenReady().then(() => {
  4. // no need to pass `sandbox: true` since `app.enableSandbox()` was called.
  5. win = new BrowserWindow()
  6. win.loadURL('http://google.com')
  7. })

预加载

An app can make customizations to sandboxed renderers using a preload script. 例如:

  1. let win
  2. app.whenReady().then(() => {
  3. win = new BrowserWindow({
  4. webPreferences: {
  5. sandbox: true,
  6. preload: path.join(app.getAppPath(), 'preload.js')
  7. }
  8. })
  9. win.loadURL('http://google.com')
  10. })

和 preload.js:

  1. // 一旦javascript上下文创建,这个文件就会被自动加载 它在一个
  2. //私有环境内运行, 可以访问 electron 渲染器的 api的子集 。 我们必须小心,
  3. //不要泄漏任何对象到全局范围!
  4. const { ipcRenderer, remote } = require('electron')
  5. const fs = remote.require('fs')
  6. // read a configuration file using the `fs` module
  7. const buf = fs.readFileSync('allowed-popup-urls.json')
  8. const allowedUrls = JSON.parse(buf.toString('utf8'))
  9. const defaultWindowOpen = window.open
  10. function customWindowOpen (url, ...args) {
  11. if (allowedUrls.indexOf(url) === -1) {
  12. ipcRenderer.sendSync('blocked-popup-notification', location.origin, url)
  13. return null
  14. }
  15. return defaultWindowOpen(url, ...args)
  16. }
  17. window.open = customWindowOpen

在预加载脚本中要注意的重要事项:

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

  • 预加载脚本可以通过 remoteipcRenderer 模块间接访问主进程中的所有 api。

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

要创建 browserify 包并将其用作预加载脚本, 应使用类似下面的内容:

  1. browserify preload/index.js \
  2. -x electron \
  3. --insert-global-vars=__filename,__dirname -o preload.js

-x 标志应该和已经在预加载作用域中公开的所有引用到的模块一起使用, 并通知 browserify 使用封闭的 require 函数。 --insert-global-vars 将确保 processBuffersetImmediate 也从封闭作用域 (通常 browserify 为这些代码注入代码) 中获取。

当前预加载作用域中提供的 require 函数公开了以下模块:

  • electron
    • crashReporter
      • desktopCapturer
      • ipcRenderer
      • nativeImage
      • remote
      • webFrame
  • 事件
  • timers
  • url

可以根据需要添加更多的electron api 以在沙箱中使用, 但主进程中的任何模块都可以通过 electron.remote.require 使用。

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 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. If disabling is not feasible, you should selectively 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.