electron 技术总结
electron是什么
此处引用官方描述:Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium 和 Node.js 到 二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发 经验。
需要注意的是electron版本是和Chromium、Node.js版本绑定的。官方给了如下对应的表格
Electron | Alpha | 测试版 | 稳定版 | Chrome | Node | 支持 |
---|---|---|---|---|---|---|
2.0.0 | – | 2018年2月1日 | 2018年5月01日 | M61 | v8.9 | 🚫 |
3.0.0 | – | 2018年6月21日 | 2018年9月18日 | M66 | v10.2 | 🚫 |
4.0.0 | – | 2018年10月11日 | 2018年2月1日 | M69 | v10.11 | 🚫 |
5.0.0 | – | 2019年3月22日 | 2019年4月24日 | M73 | v12.0 | 🚫 |
6.0.0 | – | 2019年5月01日 | 2019年7月30日 | M76 | v12.4 | 🚫 |
7.0.0 | – | 2019年8月1日 | 2019年10月22日 | M78 | v12.8 | 🚫 |
8.0.0 | – | 2019年10月24日 | 2020年2月4日 | M80 | v12.13 | 🚫 |
9.0.0 | – | 2020年2月6日 | 2020年5月19日 | M83 | v12.14 | 🚫 |
10.0.0 | – | 2020年5月21日 | 2020年8月25日 | M85 | v12.16 | 🚫 |
11.0.0 | – | 2020年8月27日 | 2020年11月17日 | M87 | v12.18 | 🚫 |
12.0.0 | – | 2020年11月19日 | 2021年3月2日 | M89 | v14.16 | 🚫 |
13.0.0 | – | 2021年3月4日 | 2021年5月25日 | M91 | v14.16 | 🚫 |
14.0.0 | – | 2021年5月27日 | 2021年8月31日 | M93 | v14.17 | 🚫 |
15.0.0 | 2021年7月20日 | 2021年9月01日 | 2021年9月21日 | M94 | v16.5 | 🚫 |
16.0.0 | 2021年9月23日 | 2021年10月20日 | 2021年11月16日 | M96 | v16.9 | 🚫 |
17.0.0 | 2021年11月18日 | 2022年1月6日 | 2022年2月1日 | M98 | v16.13 | 🚫 |
18.0.0 | 2022年2月3日 | 2022年3月3日 | 2022年3月29日 | M100 | v16.13 | ✅ |
19.0.0 | 2022年3月31日 | 2022年4月26日 | 2022年5月24日 | M102 | v16.14 | ✅ |
20.0.0 | 2022年5月26日 | 2022年6月21日 | 2022年8月2日 | M104 | v16.15 | ✅ |
21.0.0 | 2022年8月4日 | 2022年8月30日 | 2022年9月27日 | M106 | 待定 | ✅ |
关于本地nodejs环境官方给出的如下解释:
因为 Electron 将 Node.js 嵌入到其二进制文件中,你应用运行时的 Node.js 版本与你系统中运行的 Node.js 版本无关。
但本人在实际使用过程中还是碰到了版本不兼容的问题,大概是自己用的nodejs版本太低了(v10.13.0),官方建议使用最新的LTS版本,我使用的是nodejs v16.16.0,electron版本20.0
electron v1.8 升级到 electron v20.0
背景:接手的时候就发现项目中使用的版本太低,仔细研究后更发现页面上能轻易拿到执行任意命令的系统权限,虽然项目没有外链,但还是太危险了,所以根据官方给出的安全建议以及项目实际情况进行了升级。因为升级主要是因为安全问题,所以略过安装步骤,以下全部内容都是在探讨安全问题。
升级前使用加载一个index.html文件传入url参数EntryURL,在index.html中使用iframe嵌套页面的方式加载页面,大致代码如下:
mainWindow = new BrowserWindow({
width: screenSize.width,
height: screenSize.height,
title: config.DefaultTitle + '-v' + packageJSON.version,
backgroundColor: '#ffffff',
show: false,
webPreferences: {
partition: 'session' + new Date().getTime(),
webSecurity: false,
nodeIntegration: true,
backgroundThrottling: false
}
})
mainWindow.once('ready-to-show', () => {
showTips = false;
mainWindow.show();
})
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes: true,
search: `entry_url=${encodeURIComponent(config.EntryURL)}&mac=${macAddress}&serial=${diskSerialNum}`,
}))
在业务逻辑页面使用require获取electron对客户端进行一系列操作——通知客户端图标闪动、写入日志、写入本地配置等等。
top.require('electron')
在页面上能拿到electron对象,那么能做的事就太多太多了,这种方式安全性太低了,根据官方给出的建议,electron版本升级,并更改为使用preload预加载的方式暴露给页面有限的api接口的形式实现业务逻辑,代码如下:
mainWindow = new BrowserWindow({
width: screenSize.width,
height: screenSize.height,
title: config.DefaultTitle + '-v' + packageJSON.version,
// icon: path.join(__dirname, 'icon', `favicon.png`),
backgroundColor: '#ffffff',
show: false,
autoHideMenuBar:true,
webPreferences: {
partition: 'session' + new Date().getTime(),
webSecurity: true,
nodeIntegration: true,
contextIsolation:true,
backgroundThrottling: false,
spellcheck : false,
preload: path.join(__dirname, 'preload.js'),
}
})
mainWindow.once('ready-to-show', () => {
showTips = false;
loadingWindow.hide();
loadingWindow.close();
mainWindow.show();
})
mainWindow.loadURL(config.EntryURL);
引用官方给出的参数解释(https://www.electronjs.org/zh/docs/latest/api/browser-window):
- webPreferences Object (可选) - 网页功能设置。
- nodeIntegration boolean (可选) - 是否启用Node integration. 默认值为 false.
- preload string (可选) -在页面运行其他脚本之前预先加载指定的脚本 无论页面是否集成Node, 此脚本都可以访问所有Node API 脚本路径为文件的绝对路径。 当 node integration 关闭时, 预加载的脚本将从全局范围重新引入node的全局引用标志
- session Session (可选) - 设置页面的 session 而不是直接忽略 Session 对象, 也可用 partition 选项来代替,它接受一个 partition 字符串. 同时设置了session 和 partition时, session 的优先级更高. 默认使用默认的 session.
- partition string (optional) - 通过 session 的 partition 字符串来设置界面session. 如果 partition 以 persist:开头, 该页面将使用持续的 session,并在所有页面生效,且使用同一个partition. 如果没有 persist: 前缀, 页面将使用 in-memory session. 通过分配相同的 partition, 多个页可以共享同一会话。 默认使用默认的 session.
- webSecurity boolean (可选) - 当设置为 false, 它将禁用同源策略 (通常用来测试网站), 如果此选项不是由开发者设置的,还会把 allowRunningInsecureContent设置为 true. 默认值为 true。
- contextIsolation boolean (可选) - 是否在独立 JavaScript 环境中运行 Electron API和指定的preload 脚本. 默认为 true。 预加载脚本所运行的上下文环境只能访问其自身专用的文档和全局窗口,其自身一系列内置的JavaScript (Array, Object, JSON, 等等) 也是如此,这些对于已加载的内容都是不可见的。 Electron API 将只在预加载脚本中可用,在已加载页面中不可用。 这个选项应被用于加载可能不被信任的远程内容时来确保加载的内容无法篡改预加载脚本和任何正在使用的Electron api。 该选项使用的是与Chrome内容脚本相同的技术。 你可以在开发者工具Console选项卡内顶部组合框中选择 'Electron Isolated Context’条目来访问这个上下文。
引用官方安全准则:
每当你从不被信任的来源(如一个远程服务器)获取代码并在本地执行,其中就存在安全性问题。 例如在默认的 BrowserWindow中显示一个远程网站. 如果攻击者以某种方式设法改变所述内容 (通过直接攻击源或者通过在应用和实际目的地之间进行攻击) ,他们将能够在用户的机器上执行本地代码。
:::警告
无论如何,在启用Node.js集成的情况下,你都不该加载并执行远程代码。 相反,只使用本地文件(和您的应用打包在一起)来执行Node.js代码 如果你想要显示远程内容,请使用 Tag或者 BrowserView,并确保禁用 nodeIntegration 并启用 contextIsolation
引用官方安全建议清单:
- 只加载安全的内容
- 禁止在所有渲染器中使用Node.js集成显示远程内容
- 在所有渲染器中启用上下文隔离
- 启用进程沙盒化
- 在所有加载远程内容的会话中使用 ses.setPermissionRequestHandler().
- 不要禁用 webSecurity
- 定义一个Content-Security-Policy并设置限制规则(如:script-src ‘self’)
- 不要设置 allowRunningInsecureContent 为 true
- 不要开启实验性功能
- 不要使用enableBlinkFeatures
- webview:不要使用 allowpopups
- webview:验证选项与参数
- 禁用或限制网页跳转
- 禁用或限制新窗口创建
- 不要对不可信的内容使用 shell.openExternal
- 使用当前版本的 Electron
目前项目还有优化的空间(上方 7 13)