sandbox
sandbox 选项
使用可在Chromium操作系统沙箱内运行的渲染器创建浏览器窗口。启用此选项后,渲染器必须通过IPC与主进程通信以访问节点API。但是,为了启用Chromium OS沙箱,电子必须使用--enable-sandbox
命令行参数运行。
Chromium的一个关键安全功能是所有闪烁渲染/ JavaScript代码都在沙盒中执行。此沙箱使用特定于操作系统的功能来确保渲染程序中的漏洞利用不会损害系统。
换句话说,当沙箱启用时,渲染器只能通过将任务委托给主进程通过IPC来更改系统。以下是有关沙盒的更多信息。
由于电子的一个主要特性是能够在渲染过程中运行node.js(使用Web技术开发桌面应用程序变得更容易),因此沙箱被电子禁用。这是因为大多数node.js API需要系统访问。require()
例如,没有文件系统权限是不可能的,这在沙盒环境中是不可用的。
通常,这对于桌面应用程序来说不是问题,因为代码总是可信的,但与用于显示不可信网页内容的铬相比,它使得电子安全性低于铬。对于需要更高安全性的应用,该sandbox
标志将强制电子产生与沙盒兼容的经典铬渲染器。
沙盒渲染器没有运行node.js环境,也没有将node.js JavaScript API公开到客户端代码。唯一的例外是预加载脚本,它可以访问电子渲染器API的子集。
另一个区别是,沙盒渲染器不会修改任何默认的JavaScript API。因此,一些API window.open
将像铬一样工作(即它们不会返回BrowserWindowProxy
)。
例
要创建一个沙盒窗口,只需传递sandbox: true
给webPreferences
:
let win
app.on('ready', () => {
win = new BrowserWindow{
webPreferences: {
sandbox: true
}
})
w.loadURL('http://google.com')
})
在上面的代码中BrowserWindow
,创建的代码已禁用了node.js,并且只能通过IPC进行通信。使用此选项可以阻止电子在渲染器中创建node.js运行时。此外,在这个新窗口中window.open
遵循本地行为(默认情况下,电子创建一个BrowserWindow
并返回此通道的代理window.open
)。
需要注意的是,这个选项本身不会启用操作系统强制的沙箱。要启用此功能,--enable-sandbox
命令行参数必须传递给电子,这将强制sandbox: true
所有BrowserWindow
实例。
要在不使整个应用程序位于sandbox:true
沙盒中的情况下启用操作系统强制沙箱BrowserWindow
或进行webview
处理,必须将--enable-mixed-sandbox
命令行参数传递给电子。此选项目前仅在macOS和Windows上受支持。
let win
app.on('ready', () => {
// no need to pass `sandbox: true` since `--enable-sandbox` was enabled.
win = new BrowserWindow()
w.loadURL('http://google.com')
})
请注意,调用app.commandLine.appendSwitch('--enable-sandbox')
电子/节点启动代码后,可能会更改铬沙盒设置,这是不够的。交换机必须通过命令行传递给electron:
electron --enable-sandbox app.js
OS沙箱不可能只对某些渲染器有效,如果--enable-sandbox
启用,则不能创建普通的电子窗口。
如果需要在一个应用程序中混合沙箱渲染器和非沙箱渲染器,只需省略--enable-sandbox
争论。如果没有此参数,则使用sandbox: true
将仍然禁用node.js,并仅通过IPC进行通信,而IPC本身已经是安全POV的一个增益。
预压
应用程序可以使用预加载脚本对沙箱渲染器进行自定义。下面是一个例子:
let win
app.on('ready', () => {
win = new BrowserWindow{
webPreferences: {
sandbox: true,
preload: 'preload.js'
}
})
w.loadURL('http://google.com')
})
和preload.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
在预加载脚本中需要注意的重要事项:
- 尽管沙箱渲染器不具备的node.js运行,却依然能够获得有限的节点般的环境:
Buffer
,process
,setImmediate
和require
可用。
- 预加载脚本可以通过
remote
和ipcRenderer
模块间接访问主进程中的所有API 。这是如何fs
(以上使用)和其他模块实现的:它们是主流程中远程对应方的代理。
- 预加载脚本必须包含在单个脚本中,但可以通过使用像browserify这样的工具来使用多个模块组成复杂的预加载代码,如下所述。事实上,browserify已经被电子用来为预加载脚本提供类似节点的环境。
要创建一个浏览器化包并将其用作预加载脚本,应该使用以下内容:
browserify preload/index.js \
-x electron \
-x fs \
--insert-global-vars=__filename,__dirname -o preload.js
-x
标志应该与已经在预加载范围中暴露的任何必需模块一起使用,并告诉browserify使用require
它的封闭功能。--insert-global-vars
将确保process
,Buffer
并且setImmediate
也从封闭范围(通常browserify注入代码为那些)服用。
目前require
预加载范围中提供的函数公开以下模块:
child_process
electron
(crashReporter,remote和ipcRenderer)
fs
os
timers
url
可能会根据需要增加更多,以在沙箱中公开更多电子API,但主流程中的任何模块都可以使用electron.remote.require
。
状态
请sandbox
小心使用该选项,因为它仍然是一个实验性功能。我们仍然没有意识到将某些电子渲染器API暴露给预加载脚本的安全影响,但在渲染不受信任的内容之前,需要考虑以下事项:
- 预加载脚本可能会意外泄露特权API到不可信代码。
- V8引擎中的一些错误可能允许恶意代码访问渲染器预加载API,从而有效授予通过
remote
模块对系统的完全访问权限。
由于在电子中渲染不受信任的内容仍然是未知的领域,因此暴露于沙盒预加载脚本的API应该被认为比其他电子API更不稳定,并且可能会发生重大更改以解决安全问题。
一个应该大大提高安全性的计划增强功能是默认情况下阻止来自沙盒渲染器的IPC消息,从而允许主进程显式定义渲染器允许发送的一组消息。