添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

使用 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/falsefalse渲染进程集成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与主进程通信。
contextIsolationtrue/falsetrue开启JS上下文隔离默认开启,即便使用了 nodeIntegration: false, 要实现真正的强隔离并且防止使用 Node.js 的功能, contextIsolation 也 必须 开启。一般情况下结合nodeIntegration一起使用。
webSecuritytrue/falsetrue安全性功能在渲染进程(BrowserWindow、BrowserView 和 <webview>)禁用webSecurity,这将使得来自其他站点的非安全代码被执行。
allowRunningInsecureContenttrue/falsefalse允许运行不安全内容默认情况下,Electron不允许网站在HTTPS中加载或执行非安全源(HTTP) 中的脚本代码、CSS或插件。 将allowRunningInsecureContent属性设为true将禁用这种保护。
experimentalFeaturestrue/falsefalse实验性功能实验性功能是实验性的,尚未对所有 Chromium 用户启用。
enableBlinkFeaturesBlink渲染引擎特性通常来说,某个特性默认不被开启肯定有其合理的原因。 针对特定特性的合理使用场景是存在的。 作为开发者,你应该非常明白你为何要开启它,有什么后果,以及对你应用安全性的影响。 在任何情况下都不应该推测性的开启特性。

(二)加载安全内容

在渲染进程中只加载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').URL
session
  .fromPartition('some-partition')
  .setPermissionRequestHandler((webContents, permission, callback) => {
    const parsedUrl = new URL(webContents.getURL())
    if (permission === 'notifications') {
      // Approves the permissions request
      callback(true)
    // Verify URL
    if (parsedUrl.protocol !== 'https:' || parsedUrl.host !== 'example.com') {
      // Denies the permissions request
      return 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 legitimate
    delete webPreferences.preload
    // Disable Node.js integration
    webPreferences.nodeIntegration = false
    // Verify URL being loaded
    if (!params.src.startsWith('https://example.com/')) {
      event.preventDefault()

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

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

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

main.js (Main Process)

const URL = require('url').URL
app.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 allowlist
  if ((new URL(frame.url)).host === 'electronjs.org') return true;
  return false;
                    使用 Electron 很重要的一点是要理解 Electron 不是一个 Web 浏览器。它允许您使用熟悉的 Web 技术构建功能丰富的桌面应用程序,但是您的代码具有更强大的功能。JavaScript 可以访问文件系统,用户 shell 等。这允许您构建更高质量的本机应用程序,但是内在的安全风险会随着授予您的代码的额外权力而增加。
有时可以将参数传递给Electron加载脚本很方便
该模块使用Electron 2.xx中引入的 API,它不适用于较早的发行版!
addPreloadWithParams(modulePath, exportName[, params, session])
modulePath
要在加载加载的文件的路径
exportName
要执行的导出名称
 传递给默认导出功能的参数
将加载脚本添加到的会话
session.defaultSession
注意事项:
 渲染器的所有params都是序列化的,因此不要期望通过JSON.parse(JSON.stringify(data))无法生存的任何东西。
 电子加载脚本通过命令行参数传递给渲染器。 可以通过这种方式传递的数据量可能受到限制。 如果您
				
Electron14以上版本中main.js、renderer.js及preload.js关系和消息调用 Electron框架中主要分成三块,第一个是主框架(相当于CEF frame),它将内嵌index.html页面,第二个是BrowserWindow,由外框架生成,负责向用户呈现html,第三个是内含的preload.js它负责沟通主框架和内嵌页面之间的通信。 在14版本之前,主框架会开放出一个remote模块,它负责向index.html开放Node.js的All of the Node.js API
最近手头的 electron 项目需要做一个报告导出的功能,导出时要弹出个页面,可让用户自行补全相应的字段。 由于公司已有现成的笔录工具,现直接将其集成进来,用 webview 直接展示其笔录页面,将已有的值传给笔录。 webview 简介 electron 的 webview 标签时基于 Chromium webview ,由于 Chromium 的架构变化巨大,会影响 electron webview 的稳定性,包括呈现、导航和事件路由。所以 electron 团队不建议使用 webview 标 Electron基于Chromium所以可以像浏览器一样加载任何第三方网页,并且其可以对网页注入脚本,将JavaScript代码注入到目标网页中并执行就像网页开发者自己开发的一样。脚本注入后可以访问该页面内的任何内容,包括网页的样式、服务端接口、Cookie等,并且你注入的脚本还可以通过Node.js访问系统资源,例如可以将网页中获取的资源直接保存到本地磁盘中。 脚本注入实践 1.通过preload文件注入脚本 首先使用Vue CLI Plugin Electron Builder创建Elec
Electron为网页注入js加载准备工作main.jspreload.jspackage.json遇到的问题下一步的方向 首先我下载了一个 electron-quick-start 并安装 node.js 找到一个网页,我选择的是一个快速制作插画的网站 Picrew main.js 在main.js里可以修改弹窗的各种属性,我们需要的是 function createWindow () { const mainWindow = new BrowserWindow({ webPref
1. 主进程中无需任何配置就可以使用nodejs模块 const fs=require('fs') fs.readFile('package.json',(err,data)=>{ if(err){ console.log(err) return console.log(data.toString()) console.log(JSON.parse(data.toString()).na
注意:本文基于electron-vue项目(在vue2项目上运行vue add electron-builder命令搭建而成) 并在electron8.5.5及9.0.0测试过 在webview中开发者可以使用preload加载并执行js文件 官方文档 网上以及官方的很多例子都是传入一个相对路径的String,并没有以file://开头。然而我这里只能传入String字符串并且是file协议的,electron会检测是不是以file://开头,不符合就会报错(不知道是不是我配置有问题还是啥…反正只要不以
在使用mac 进行开发的时候 npm start Error: dlopen(/usr/local/study_data/python/projects/node-pyrunner-quick-start-main/node_modules/.store/node-pyrunner@1.0.6/node_modules/node-pyrunner/addons/3.10.10/darwin/x64/pyrunner.node, 1): Library not loaded: /Users/coderlee/.pyenv/versions/3.10.10/lib/libpython3.10.dylib Referenced from: /usr/local/study_data/python/projects/node-pyrunner-quick-start-main/node_modules/.store/node-pyrunner@1.0.6/node_modules/node-pyrunner/addons/3.10.10/darwin/x64/pyrunner.node Reason: image not found 指向的是index.js的 /* pyrunner addon object */ this._addon = require(`./addons/${this.config['python_version']}/${process.platform}/${process.arch}/pyrunner`);
Python使用Cython+MinGW编译Pyd动态链接库 Walterall: 你好,我按照步骤操作,但是编译依然用的是vc,请问应该是哪里有问题? Nodejs调用python的几种方案 码力巨能编: nodepyrunner是node-pyrunner注入的模块,app.py在node-pyrunner里面执行才能使用这个模块。 Nodejs调用python的几种方案 dahuangfeng2hao: app.py中import nodepyrunner找不到这个类库