使用 Electron 很重要的一点是要理解 Electron 不是一个 Web 浏览器。 它允许您使用熟悉的 Web 技术构建功能丰富的桌面应用程序,但是您的代码具有更强大的功能。 JavaScript 可以访问文件系统,用户 shell 等。 这允许您构建更高质量的本机应用程序,但是内在的安全风险会随着授予您的代码的额外权力而增加。

事实上,最流行的 Electron 应用程序(Atom,Slack,Visual Studio Code 等) 主要显示本地内容(即使有远程内容也是无 Node 的、受信任的、安全的内容) - 如果您的应用程序要运行在线的源代码,那么您需要确保源代码不是恶意的。

一、preload

preload是electron的预加载机制,可以理解为在electron创建时将nodejs环境加载到渲染进程中使用。程序的进程是相互独立的,electron中渲染进程和主进程的协同工作一般采用IPC进程通信或者在渲染进程中集成node环境,还有早期比较低效的remote模块方式使用node环境。除非保证渲染进程的JavaScript都是可信安全的,否则不推荐在渲染进程集成node环境。使用preload的目的是在electron中不开启node环境集成情况下使用node的模块,避免不安全的JavaScript代码随意使用node环境。
preload的工作是在创建窗体时预加载需要node模块到渲染进程,然后以API方式暴露给渲染进程调用,preload共享渲染进程的window、document对象,因此preload可以轻松操作DOM,而渲染进程不共享preload的global对象。

preload.js

const { contextBridge, ipcRenderer} = require('electron');
const fs = require('fs'); contextBridge.exposeInMainWorld('fsApi', {writeFile: (filename, text, callback) => {fs.writeFile(filename, text, callback);},readFile: (filename, encode,  callback) => {fs.readFile(filename, encode, callback);},unlink: (filename, callback) => {fs.unlink(filename, callback);}
});contextBridge.exposeInMainWorld('ipcRendererApi', {send: (channel, args) => ipcRenderer.send(channel, args),once: (channel, listener) => ipcRenderer.once(channel, listener),on: (channel, listener) => ipcRenderer.on(channel, listener),
});

调用预加载API

ipcRendererApi.send('close', args)

二、安全策略

(一)webPreferences属性

Electron安全策略几乎都在webPreferences属性中设置,通过属性来开启或者关闭渲染进程相关安全选项。

属性 默认 作用 说明
nodeIntegration

nodeIntegrationInWorker
true/false false 渲染进程集成node环境 默认关闭,当渲染进程加载远程内容时必须关闭,远程内容不确定性,避免跳过渲染进程在node环境下执行JS脚本。webview中同样不推荐使用nodeIntegration属性。<webview nodeIntegration src="page.html"></webview>
preload 预加载脚本 当禁用Node.js集成时,你依然可以暴露API给你的站点以使用Node.js的模块功能或特性。即预加载node模块到渲染进程使用,另外预加载的模块可以直接访问渲染进程的DOM。
electron早期继承remote模块,用于在渲染进程调用主进程的功能,当前最新的electron已经不是默认模块需要另外安装,使用remote模块相比较于IPC通信更低效,建议使用IPC与主进程通信。
contextIsolation true/false true 开启JS上下文隔离 默认开启,即便使用了 nodeIntegration: false, 要实现真正的强隔离并且防止使用 Node.js 的功能, contextIsolation 也 必须 开启。一般情况下结合nodeIntegration一起使用。
webSecurity true/false true 安全性功能 在渲染进程(BrowserWindow、BrowserView 和 <webview>)禁用webSecurity,这将使得来自其他站点的非安全代码被执行。
allowRunningInsecureContent true/false false 允许运行不安全内容 默认情况下,Electron不允许网站在HTTPS中加载或执行非安全源(HTTP) 中的脚本代码、CSS或插件。 将allowRunningInsecureContent属性设为true将禁用这种保护。
experimentalFeatures true/false false 实验性功能 实验性功能是实验性的,尚未对所有 Chromium 用户启用。
enableBlinkFeatures Blink渲染引擎特性 通常来说,某个特性默认不被开启肯定有其合理的原因。 针对特定特性的合理使用场景是存在的。 作为开发者,你应该非常明白你为何要开启它,有什么后果,以及对你应用安全性的影响。 在任何情况下都不应该推测性的开启特性。

(二)加载安全内容

在渲染进程中只加载HTTPS的安全内容

main.js (Main Process)

// 不推荐
browserWindow.loadURL ('http://example.com')
// 推荐
browserWindow.loadURL ('https://example.com')

index.html (Renderer Process)

<!-- 不推荐 -->
<script crossorigin src="http://example.com/react.js"></script>
<link rel="stylesheet" href="http://example.com/style.css"><!-- 推荐 -->
<script crossorigin src="https://example.com/react.js"></script>
<link rel="stylesheet" href="https://example.com/style.css">

(三)启用进程沙盒化

您应该在所有渲染器中启用沙盒。 不建议在一个未启动沙盒的进程(包括主进程)中加载、阅读或处理任何不信任的内容。Electron20及以后的版本默认开启沙盒,这导致preload加载nodejs的插件报错,在确保当前程序的代码安全性的前提下可以手动关闭沙盒。详情

(四)限制请求会话权限

通过session来过滤请求协议或者域名,electron该API基于Chromium permissions API实现。

main.js (Main Process)

const { session } = require('electron')
const URL = require('url').URLsession.fromPartition('some-partition').setPermissionRequestHandler((webContents, permission, callback) => {const parsedUrl = new URL(webContents.getURL())if (permission === 'notifications') {// Approves the permissions requestcallback(true)}// Verify URLif (parsedUrl.protocol !== 'https:' || parsedUrl.host !== 'example.com') {// Denies the permissions requestreturn callback(false)}})

(五)Content-Security-Policy(CSP)

CSP是HTTP协议标头,限制允许渲染进程执行的脚本,是应对跨站脚本攻击和数据注入攻击的策略。

// 不推荐
Content-Security-Policy: '*'// 推荐
Content-Security-Policy: script-src 'self' https://apis.example.com

main.js (Main Process)

const { session } = require('electron')session.defaultSession.webRequest.onHeadersReceived((details, callback) => {callback({responseHeaders: {...details.responseHeaders,'Content-Security-Policy': ['default-src \'none\'']}})
})

index.html (Renderer Process)

<meta http-equiv="Content-Security-Policy" content="default-src 'none'">

仅适用于HTTP协议

(六)Webview的allowpopups属性

index.html (Renderer Process)

<!-- 不推荐 -->
<webview allowpopups src="page.html"></webview><!-- 推荐 -->
<webview src="page.html"></webview>

(七)Webview标签选项

渲染进程通过标签创建的webview没有node继承,而从主进程创建的webview可以加成node。在 标签生效前,Electron将产生一个will-attach-webview事件到webContents中。 利用这个事件来阻止可能含有不安全选项的 webViews 创建。

main.js (Main Process)

app.on('web-contents-created', (event, contents) => {contents.on('will-attach-webview', (event, webPreferences, params) => {// Strip away preload scripts if unused or verify their location is legitimatedelete webPreferences.preload// Disable Node.js integrationwebPreferences.nodeIntegration = false// Verify URL being loadedif (!params.src.startsWith('https://example.com/')) {event.preventDefault()}})
})

(八)禁用或限制网页跳转

该功能主要是防止跳转到指定页面后执行JS,从而存在跨站点攻击隐患,即便是关闭node集成开启上下文隔离的情况下,也不应该允许任意网页跳转。

如果您的应用不需要导航,您可以在 will-navigate 处理器中调用 event.preventDefault()。 如果您知道您的应用程序可能会导航到哪些界面,请在事件处理器中检查URL,并且仅当它与您预期的URL匹配时才进行导航。

main.js (Main Process)

const URL = require('url').URLapp.on('web-contents-created', (event, contents) => {contents.on('will-navigate', (event, navigationUrl) => {const parsedUrl = new URL(navigationUrl)if (parsedUrl.origin !== 'https://example.com') {event.preventDefault()}})
})

(九)禁止或限制新窗口创建

当打开新窗口时,注册事件来处理即将打开新窗口的URL,从而过滤掉不安全的url打开新窗口。

main.js (Main Process)

const { shell } = require('electron')app.on('web-contents-created', (event, contents) => {contents.setWindowOpenHandler(({ url }) => {// 在此示例中我们要求操作系统// 在默认浏览器中打开此事件的 url。//// 关于哪些URL应该被允许通过shell.openExternal打开,// 请参照以下项目。if (isSafeForExternalOpen(url)) {setImmediate(() => {shell.openExternal(url)})}return { action: 'deny' }})
})

(十)shell.openExternal

shell.openExternal可以用来执行任意命令,不受信任的内容不要使用该API。

main.js (Main Process)

//  不好
const { shell } = require('electron')
shell.openExternal(USER_CONTROLLED_DATA_HERE)
//  好
const { shell } = require('electron')
shell.openExternal('https://example.com/index.html')

(十一)验证IPC消息发送者

任何窗口都可以向主进程发送消息,主进程回复渲染进程时,应该确定发送者身份。

main.js (Main Process)

// Bad
ipcMain.handle('get-secrets', () => {return getSecrets();
});// Good
ipcMain.handle('get-secrets', (e) => {if (!validateSender(e.senderFrame)) return null;return getSecrets();
});function validateSender(frame) {// Value the host of the URL using an actual URL parser and an allowlistif ((new URL(frame.url)).host === 'electronjs.org') return true;return false;
}

Electron使用preload预加载及安全策略相关推荐

  1. 不加载执行js_前端性能优化:preload 预加载页面资源

    网上看到一篇来自蚂蚁金服数据体验团队的文章,觉得不错,分享给大伙:https://juejin.im/post/5a7fb09bf265da4e8e785c38 本文主要介绍preload的使用,以及 ...

  2. 前端性能优化:dns-prefetch和preload预加载资源

    前端页面加载的时候我们有时会用到一些非模块化的库都是用cdn引入的,如果体积很大的话会阻塞页面的加载,并且这个库可能只在特定页面调用,这样体验很不好 这里可以用dns-prefetch按需加载解决. ...

  3. uniapp开发h5页面实现图片预加载功能

    背景 h5页面,很多时候存在大量的图片.动画,这些都需要下载大量的静态资源,如果我们直接打开页面,会发现部分图片正在加载或者还未下载的现象,严重影响体验效果. 为了解决这个问题,我们需要进行图片预加载 ...

  4. 【第1159期】CSS预加载Preload

    前言 看天气预报,今天好多地方都开始下雪了.今日早读文章由@李斌分享. 正文从这开始- Preload 作为一个新的web标准,旨在提高性能和为web开发人员提供更细粒度的加载控制.Preload使开 ...

  5. 通过rel=preload进行内容预加载

    proload基础 <link> 元素的 rel 属性的属性值preload能够让你在你的HTML页面中 <head>元素内部书写一些声明式的资源获取请求,可以指明哪些资源是在 ...

  6. 图片预加载插件 preLoad.js

    1.preLoad.js插件 1 /*! 2 * preLoad.js v1.0 3 * (c) 2017 Meng Fangui 4 * Released under the MIT License ...

  7. GORM v2 关联预加载Preload和Joins的区别

    前言 本文中使用到的数据表结构以及GORM版本的区分详见以下文章:GORM v2 一对一关联查询使用(Belongs To .Has One) 执行区别 调用gorm的Debug方法打印一下一对一关联 ...

  8. 纯web项目不能使用mui.preload进行页面预加载的解决办法

    首先: 纯web项目不能使用mui.preload进行页面预加载的, 比如[基于微信的web项目](http://ask.dcloud.net.cn/question/20644) 怎么办呢? 自己写 ...

  9. 预加载属性 preload 与 prefetch 区别

    原文链接:https://waynegong.cn/posts/40528.html TLDR: preload 告诉浏览器立即加载资源; prefetch 告诉浏览器在空闲时才开始加载资源: pre ...

最新文章

  1. tensorflow 1
  2. mongodb模糊查询 php7_详解php7如何实现MongoDB模糊查询
  3. 10个奇幻的HTML5和Javascript效果
  4. 搞怪菜鸟加入域全程图解[为企业部署Windows Server 2008系列十二]
  5. 【笔试题】京东2017秋招笔试真题
  6. Hibernate开发和对象状态
  7. 安卓用targetSdk来兼容各个版本
  8. 推荐一个CSLab------英真时代(非广告,真心的)
  9. (可更新)计算机算法设计与分析 第4版 (王晓东) 课后答案[1-9章]
  10. 3D打印是什么?如何工作的?
  11. HBase的rowKey设计技巧
  12. 计算机网路实验四 IP协议分析
  13. python代码敲写英雄联盟排列
  14. [面面面]一篇搞定计算机面试常见知识点(10w字更新中)
  15. 技术岗找工作简历撰写方法
  16. 【认识硬件】之 水泥电阻
  17. 机器学习——基础知识
  18. IDEA敏感数据检测实践整理
  19. 服务器上显示存储脱机,服务器硬盘脱机状态
  20. Linux压缩解压tar.gz和zip包命令汇总

热门文章

  1. 【Python】深入理解python格式化输出
  2. Thinkphp:使用setInc、setDec方法时报错
  3. SAP激活表时报错指定参考表和参考字段/建表有MEINS/QUAN类型字段不能激活
  4. 组合式 API 的优势
  5. 厉害了!Spring Boot + Vue 开发移动端商城(附源码、文档、视频)!
  6. 计算机检查例外 错误类型,事件查看器出现致命硬件错误,电脑频繁死机
  7. 爱“搬家”的欧宗洪 抱薪救火的融信中国
  8. 向量点乘的图形学意义
  9. word、excel、ppt转pdf
  10. 【Spring从成神到升仙系列 二】2023年再不会 IOC 源码,就要被淘汰了