Electron 应用安全加固完全指南:基于官方 Security 清单的 20 条防护实践
本文基于 Electron 官方仓库 docs/tutorial/security.md 的安全文档展开,面向想要构建可安全上线的桌面应用开发者。文章以官方推荐的 20 条安全检查清单为骨架,结合仓库内渲染进程安全告警实现 lib/renderer/security-warnings.ts、WebPreferences 默认值解析 shell/browser/web_contents_preferences.cc 等源码证据,帮助你理解每一项加固措施"为什么必须做、怎么做、不做的后果是什么",最终交付一个不把用户机器暴露在远程代码执行(RCE)风险之下的 Electron 应用。
从 Web 到桌面:理解 Electron 的"权力与责任"
作为 Web 开发者,我们习惯了浏览器提供的安全网:网页代码运行在沙箱中,权限极其有限,而且背后有一个庞大团队维护、能快速响应新威胁的浏览器。但在 Electron 中情况完全不同——Electron 不是 Web 浏览器。它允许你用熟悉的 Web 技术构建功能丰富的桌面应用,代价是你的 JavaScript 拥有了远超网页的能力:可以访问文件系统、用户 Shell 等本地资源。这让你能构建高质量的原生应用,但固有安全风险也随之放大。
因此必须牢记:展示来自不可信来源的任意内容,是一种 Electron 并不打算替你兜底的严重安全风险。事实上最流行的 Electron 应用(Atom、Slack、Visual Studio Code 等)主要展示的是本地内容,或关闭 Node 集成的可信远程内容——如果你的应用要执行来自在线来源的代码,确保代码无恶意是你自己的责任。
安全漏洞上报:如何正确上报 Electron 漏洞,参见 SECURITY.md;上游 Chromium 漏洞方面,Electron 始终跟随交替的 Chromium 版本,可参考 Electron 发布节奏。
安全总则:三条人人有责的基线
你应用的最终安全水平 = Chromium 的安全性 × Node.js 的安全性 × Electron 自身的安全性 × 所有 NPM 依赖的安全性 × 你自己的代码质量。因此需要遵循三条重要实践:
-
让应用始终跟随最新的 Electron 框架版本。 发布产品时,你同时发布了一个由 Electron、Chromium 共享库和 Node.js 组成的捆绑体。任何组件的漏洞都可能波及你的应用。升级到最新版,意味着类似 nodeIntegration 绕过 这类严重漏洞已被修补、无法在你的应用中被利用。详见下文第 16 条。
-
审查你的依赖。 NPM 提供海量可复用包,但选择可信的第三方库是你的责任。使用存在已知漏洞的过期库、或依赖维护不善的代码,会让应用安全亮红灯。
-
采用安全的编码实践。 应用的第一道防线是你自己的代码。像跨站脚本(XSS)这类常见 Web 漏洞在 Electron 应用中危害更大,因此强烈建议遵循安全开发最佳实践并开展安全测试。
不可信内容的隔离:安全心智模型
无论何时,只要你的应用"从不可信来源(如远程服务器)接收代码并在本地执行",就存在安全问题。例如:在默认 BrowserWindow 中展示一个远程网站,一旦攻击者篡改了该内容(无论是直接攻击源站,还是"中间人"拦截在你的应用与目标之间),他们就能在用户机器上执行原生代码。
⚠️ 绝对禁止在启用 Node.js 集成的情况下加载并执行远程代码。执行 Node.js 代码只应使用本地文件(随应用一起打包)。展示远程内容请使用
<webview>标签或WebContentsView,并务必关闭nodeIntegration、开启contextIsolation。
开发期的"安全哨兵":Electron 安全告警
Electron 会在开发者控制台打印安全告警与建议。这些告警只在二进制名仍是 Electron 时显示,代表开发者此刻正盯着控制台。这一点在源码中得到印证:渲染进程的告警模块 lib/renderer/security-warnings.ts 会按平台判断可执行文件路径是否仍以 electron/Electron.app 命名,从而决定是否输出告警。
你可以强制开关这些告警,只需在 process.env 或 window 对象上设置:
ELECTRON_ENABLE_SECURITY_WARNINGS:强制开启;ELECTRON_DISABLE_SECURITY_WARNINGS:强制关闭。
process.env.ELECTRON_ENABLE_SECURITY_WARNINGS = 'true'
// 或 window.ELECTRON_ENABLE_SECURITY_WARNINGS = 'true'
在 security-warnings.ts 中可以看到这些开关的优先级实现,以及它逐条实现的检查逻辑。有意思的是,它甚至能静态探测页面中的风险项:比如通过 performance.getEntriesByType('resource') 收集所有用 HTTP/FTP 加载的不安全资源并逐一列出(对应清单第 1 条);探测 <webview allowpopups>(对应第 11 条);通过 webFrame._isEvalAllowed() 判断页面是否设了包含 unsafe-eval 的 CSP(对应第 7 条)。开发期善用这些告警,等于让 Electron 帮你做一轮自动化安全体检。
安全检查清单:20 条加固实践
按官方文档要求,至少应完成以下 20 步以提升应用安全性。每一条都给出 Why(风险原理)与 How(落地代码)。
1. 只加载安全内容
应用中所有非随包资源都应使用安全协议加载,如 HTTPS。不要用不安全的 HTTP。同理推荐用 WSS 而非 WS、用 FTPS 而非 FTP。
为什么? HTTPS 有两个核心收益:
- 数据完整性:保证数据在应用与主机之间传输时未被篡改;
- 传输加密:加密用户与目标主机之间的流量,让窃听更难。
怎么做?
// 错误
browserWindow.loadURL('http://example.com')
// 正确
browserWindow.loadURL('https://example.com')
<!-- 错误 -->
<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">
补充:源码中判断"不安全资源"时对 localhost/127.0.0.1/[::1] 做了豁免(见 security-warnings.ts),即本地开发服务器走 HTTP 不会触发告警,但生产环境必须全量 HTTPS。
2. 不要对远程内容启用 Node.js 集成
自 Electron 5.0.0 起,这已是默认行为。
在任何加载远程内容的渲染器(BrowserWindow、WebContentsView 或 <webview>)中,绝不要开启 Node.js 集成。目标是把授予远程内容的权力降到最低——即便攻击者获得了在页面上执行 JavaScript 的能力,想伤害用户也难得多。
为什么? 跨站脚本(XSS)攻击如果能让攻击者跳出渲染进程、在用户电脑上执行代码,就会升级为远程代码执行(RCE)。XSS 本身很常见但危害通常局限于所在网站;关闭 Node.js 集成正是防止 XSS 升级为 RCE 的关键。
怎么做?
// 错误
const mainWindow = new BrowserWindow({
webPreferences: {
contextIsolation: false,
nodeIntegration: true,
nodeIntegrationInWorker: true
}
})
mainWindow.loadURL('https://example.com')
// 正确
const mainWindow = new BrowserWindow({
webPreferences: {
preload: path.join(app.getAppPath(), 'preload.js')
}
})
mainWindow.loadURL('https://example.com')
<!-- 错误 -->
<webview nodeIntegration src="page.html"></webview>
<!-- 正确 -->
<webview src="page.html"></webview>
关闭 Node.js 集成并不妨碍你向网站暴露自定义 API:预加载脚本仍可访问 require 等 Node 特性,开发者可以用 contextBridge API 向远程加载的内容暴露受控 API。
3. 在所有渲染进程中开启 Context Isolation(上下文隔离)
自 Electron 12.0.0 起,这已是默认行为。
上下文隔离允许开发者让预加载脚本与 Electron API 运行在独立的 JavaScript 上下文中。实践意义是:渲染进程里的脚本无法篡改 Array.prototype.push 或 JSON.parse 这类全局对象。Electron 复用了 Chromium Content Scripts 的同一套技术。
即使设置了 nodeIntegration: false,也必须同时开启 contextIsolation 才能真正强制隔离、阻止 Node 原语被使用。
另外要警惕:对某个渲染进程设置 nodeIntegration: true(即关闭上下文隔离)同时也会关闭该进程的进程沙箱——参见下一节。
想深入了解
contextIsolation是什么、如何开启,参见专门文档 Context Isolation。
4. 开启进程沙箱
自 Electron 20.0.0 起,这已是默认行为;还可通过"全局启用沙箱"对整个应用的所有渲染进程强制开启,参见 Process Sandboxing 文档。 再次强调:关闭上下文隔离(见上)会连带关闭进程沙箱——无论默认值、
sandbox: false还是全局沙箱都是如此!
沙箱是 Chromium 特性:借助操作系统大幅限制渲染进程能访问的内容。所有渲染器都应开启沙箱。切勿在非沙箱进程(包括主进程)中加载、读取或处理任何不可信内容。
从源码可以印证沙箱与 Node 集成的绑定关系:在 web_contents_preferences.cc 中,如果没有显式设置 sandbox,则默认取 sandboxed = !(nodeIntegration || nodeIntegrationInWorker)——即只要开启了 Node 集成,进程就自动非沙箱化。这就是为什么官方文档反复强调"不要为了图省事把 nodeIntegration 打开"。
沙箱开启后渲染进程只拥有 CPU 与内存权限,需要文件系统等特权操作时必须通过 IPC 委托给主进程。关于沙箱行为与沙箱化预加载脚本可用模块的细节,参见 Process Sandboxing。
怎么做? 保持默认即可:
const mainWindow = new BrowserWindow({}) // 沙箱默认开启
5. 用 ses.setPermissionRequestHandler() 处理所有会话的权限请求
你在 Chrome 中见过权限请求——网站想使用需要用户手动批准的特性(如通知)时弹出。该 API 基于 Chromium permissions API,实现相同类型的权限。
为什么? 默认情况下,除非开发者手动配置自定义处理器,Electron 会自动批准所有权限请求。这虽然是稳健的默认值,但安全意识强的开发者应该假设完全相反的情形。
怎么做? 在 app.whenReady 之后配置(注意:session.defaultSession 只有在 app.whenReady 被调用后才可用):
const { session } = require('electron')
const { URL } = require('node:url')
session
.defaultSession
.setPermissionRequestHandler((webContents, permission, callback) => {
const parsedUrl = new URL(webContents.getURL())
if (permission === 'notifications') {
// 批准该权限请求
callback(true)
}
// 校验 URL
if (parsedUrl.protocol !== 'https:' || parsedUrl.host !== 'example.com') {
// 拒绝该权限请求
return callback(false)
}
})
从源码看,该处理器最终被桥接到 ElectronPermissionManager(shell/browser/electron_permission_manager.cc),并经由 electron_api_session.cc 暴露为 session 的方法,权限决策会统一收敛到主进程这一入口。
6. 不要关闭 webSecurity
这是 Electron 的默认值。
为什么? 关闭 webSecurity 会禁用同源策略并把 allowRunningInsecureContent 置为 true——等于允许跨域执行不安全代码。
怎么做?
// 错误
const mainWindow = new BrowserWindow({
webPreferences: {
webSecurity: false
}
})
// 正确
const mainWindow = new BrowserWindow()
<!-- 错误 -->
<webview disablewebsecurity src="page.html"></webview>
<!-- 正确 -->
<webview src="page.html"></webview>
开发期若在控制台看到 Electron Security Warning (Disabled webSecurity),说明有渲染器显式把 webSecurity 设成了 false,参见 security-warnings.ts。
7. 定义内容安全策略(Content-Security-Policy)
CSP 是抵御 XSS 与数据注入攻击的额外一层防护。建议 Electron 内加载的任何网站都启用。
为什么? CSP 允许内容服务器限制并控制 Electron 能为该网页加载哪些资源:https://example.com 只应从你定义的源加载脚本,而 https://evil.attacker.com 的脚本不应运行。
怎么做? 下面这条 CSP 允许 Electron 从当前网站与 apis.example.com 执行脚本:
// 错误
Content-Security-Policy: '*'
// 正确
Content-Security-Policy: script-src 'self' https://apis.example.com
通过 HTTP 响应头下发 CSP
Electron 尊重 Content-Security-Policy HTTP 响应头,可用 Electron 的 webRequest.onHeadersReceived 处理器设置(同样需在 app.whenReady 之后):
const { session } = require('electron')
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': ['default-src \'none\'']
}
})
})
通过 meta 标签下发 CSP
CSP 的首选传递机制是 HTTP 头。但使用 file:// 协议加载资源时无法用响应头,这时可以在页面标记里直接用 <meta> 设置策略:
<meta http-equiv="Content-Security-Policy" content="default-src 'none'">
补充:渲染进程告警逻辑会检查页面是否设置了不含 unsafe-eval 的 CSP——若未设置 CSP 或策略带 unsafe-eval,控制台会出现 Electron Security Warning (Insecure Content-Security-Policy),实现见 security-warnings.ts。
8. 不要启用 allowRunningInsecureContent
这是 Electron 的默认值。
默认情况下,Electron 不允许 HTTPS 加载的网页再从不安全源(HTTP)加载并执行脚本、CSS 或插件。把 allowRunningInsecureContent 设为 true 会关闭这层保护。HTTPS 加载首页 HTML、再用 HTTP 加载其余资源,即所谓"混合内容"(mixed content)。
为什么? HTTPS 加载保证资源真实性与完整性并加密流量(见第 1 条)。
怎么做?
// 错误
const mainWindow = new BrowserWindow({
webPreferences: {
allowRunningInsecureContent: true
}
})
// 正确
const mainWindow = new BrowserWindow({})
9. 不要启用实验性特性
这是 Electron 的默认值。
高级用户可用 experimentalFeatures 属性开启 Chromium 实验性特性。
为什么? 实验性特性顾名思义尚未面向所有 Chromium 用户启用,它们对 Electron 整体的影响大概率未经测试。存在合理的应用场景,但除非你清楚自己在做什么,否则不应开启。
怎么做?
// 错误
const mainWindow = new BrowserWindow({
webPreferences: {
experimentalFeatures: true
}
})
// 正确
const mainWindow = new BrowserWindow({})
10. 不要使用 enableBlinkFeatures
这是 Electron 的默认值。
Blink 是 Chromium 背后的渲染引擎。与 experimentalFeatures 一样,enableBlinkFeatures 允许开发者开启默认被禁用(或启用)的特定功能。
为什么? 一般而言,某个功能默认没开多半有充分理由。合理用途确实存在,但作为开发者你必须确切知道为何需要、后果是什么、如何影响应用安全——绝不要投机性地开启特性。
怎么做?
// 错误
const mainWindow = new BrowserWindow({
webPreferences: {
enableBlinkFeatures: 'ExecCommandInJavaScript'
}
})
// 正确
const mainWindow = new BrowserWindow()
11. WebView 不要使用 allowpopups
这是 Electron 的默认值。
如果你用 <webview>,其内部加载的页面/脚本可能需要打开新窗口。allowpopups 属性允许它们用 window.open() 创建新的 BrowserWindow;没有该属性,<webview> 默认不允许创建新窗口。
为什么? 遵循最小权限原则:不需要弹窗就别默认允许创建新窗口。
怎么做?
<!-- 错误 -->
<webview allowpopups src="page.html"></webview>
<!-- 正确 -->
<webview src="page.html"></webview>
渲染进程侧,告警逻辑会通过 document.querySelectorAll('[allowpopups]') 静态扫描 DOM,发现带 allowpopups 的 <webview> 即打印告警(security-warnings.ts)。
12. 创建 WebView 前校验其选项与参数
在没有 Node.js 集成的渲染进程中创建的 WebView,自身无法再开启集成;但 WebView 总会创建一个带独立 webPreferences 的渲染进程。因此最好从主进程控制新 <webview> 标签的创建,并校验其 webPreferences 没有关闭安全特性。
为什么? 由于 <webview> 活在 DOM 里,即使 Node.js 集成被关闭,网站脚本也可以创建它。Electron 允许开发者关闭各种安全特性,但大多数情况下并不需要——你不应允许新建的 <webview> 使用不同的配置。
怎么做? 在 <webview> 标签被 attach 之前,Electron 会在宿主 webContents 上触发 will-attach-webview 事件。利用它阻止可能不安全的选项:
app.on('web-contents-created', (event, contents) => {
contents.on('will-attach-webview', (event, webPreferences, params) => {
// 若不需要 preload 脚本则移除,或校验其位置是否合法
delete webPreferences.preload
// 关闭 Node.js 集成
webPreferences.nodeIntegration = false
// 校验加载的 URL
if (!params.src.startsWith('https://example.com/')) {
event.preventDefault()
}
})
})
再次提醒:这份清单只是降低风险而非消除风险。如果你的目标是展示某个网站,浏览器才是更安全的选择。
13. 禁用或限制导航
如果应用不需要导航、或只需导航到已知页面,最好把导航整体限制在该已知范围内,拒绝任何其他导航。
为什么? 导航是常见攻击向量。攻击者若能诱导你的应用离开当前页面,就可能强制它打开互联网上的任意网站。即便你的 webContents 已配置得更安全(如关闭 nodeIntegration、开启 contextIsolation),让应用打开随机网站仍会大幅降低攻击难度。常见手法是通过链接、插件或其他用户生成内容诱使用户与应用交互、使应用导航到攻击者页面。
怎么做? 无需导航时,可在 will-navigate 处理器中调用 event.preventDefault();若知道可能导航到哪些页面,则在处理器里校验 URL、只放行预期地址。
务必用 Node 的 URL 解析器做校验——简单的字符串比较容易被骗:startsWith('https://example.com') 会放行 https://example.com.attacker.com。
const { app } = require('electron')
const { URL } = require('node: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()
}
})
})
14. 禁用或限制新窗口的创建
如果应用有明确的窗口集合,最好限制运行时额外窗口的创建。
为什么? 与导航类似,新 webContents 的创建是常见攻击向量——攻击者诱使应用创建比原进程拥有更多权限的新窗口、新 frame 或其他渲染进程。对于"只开一个 BrowserWindow、运行时不需要任意数量额外窗口"的应用,禁用创建是零成本的安全增益。
怎么做? webContents 在创建新窗口前会委托给其 window open handler,处理器会收到请求打开的 url 与创建选项等参数。注册处理器监控窗口创建并拒绝任何意外的窗口创建:
const { app, 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' }
})
})
相关 API 绑定位于 lib/browser/api/web-contents.ts 的 WebContents.prototype.setWindowOpenHandler。
15. 不要对不可信内容使用 shell.openExternal
shell 模块的 openExternal API 允许用桌面原生工具打开给定协议 URI。例如 macOS 上该函数类似 open 终端命令,会按 URI 与文件类型关联打开特定应用。
为什么? 滥用 openExternal 可能危及用户主机——当它与不可信内容一起使用时,可能被利用来执行任意命令。
怎么做?
// 错误
const { shell } = require('electron')
shell.openExternal(USER_CONTROLLED_DATA_HERE)
// 正确
const { shell } = require('electron')
shell.openExternal('https://example.com/index.html')
16. 始终使用当前版本的 Electron
应尽量使用 Electron 的最新可用版本。每当新的大版本发布,尽快升级你的应用。
为什么? 用旧版 Electron、Chromium 与 Node.js 构建的应用比新版更容易成为攻击目标——旧版本的安全问题与利用方式传播更广。Chromium 与 Node.js 都是大量顶尖工程师的杰作,其安全性也经受着同等顶尖安全研究者的测试分析。多数研究者遵循负责任披露,即在公开前给官方留出修复时间。因此,运行较新版本(从而自带较新的 Chromium 与 Node.js)的应用,潜在安全问题没那么广为人知,也就更安全。
怎么做? 逐大版本迁移,同时对照 Electron 的 Breaking Changes 文档确认是否有需要更新的代码。
17. 校验所有 IPC 消息的 sender
默认应对所有 IPC 消息校验 sender,确保你没有对不可信渲染器执行操作或发送信息。
为什么? 理论上所有 Web Frame 都能向主进程发送 IPC 消息——某些场景下包括 iframe 和子窗口。如果你的 IPC 消息通过 event.reply 回传用户数据、或执行渲染器本身无法完成的特权操作,就必须确保没有在监听第三方 Web Frame。
怎么做?
// 错误
ipcMain.handle('get-secrets', () => {
return getSecrets()
})
// 正确
ipcMain.handle('get-secrets', (e) => {
if (!validateSender(e.senderFrame)) return null
return getSecrets()
})
function validateSender (frame) {
// 用真正的 URL 解析器 + 白名单校验 URL 的 host
if ((new URL(frame.url)).host === 'electronjs.org') return true
return false
}
注意这里校验的是 e.senderFrame.url 而非笼统的 sender——senderFrame 能精确到发起消息的 frame,可有效挡住 iframe 冒充。白名单判断同样要基于真正的 URL 解析器。
18. 避免 file:// 协议,优先使用自定义协议
本地页面应通过自定义协议提供,而不是 file://。
为什么? file:// 在 Electron 中获得的特权比在 Web 浏览器中更多,即便在浏览器里它也与 http/https URL 处理方式不同。运行在 file:// 的页面拥有访问机器上每个文件的单边权限——这意味着 XSS 可被用来加载用户机器上的任意文件。使用自定义协议则能规避这类问题:你可以把协议限制为只服务特定文件集合,并保留对"何时能加载什么"的更强控制,行为更接近经典 Web URL。
怎么做? 参考 protocol.handle 的示例学习如何用自定义协议服务文件/内容。
延伸阅读:file:// 额外特权本质上是可裁剪的。Electron 提供了 grantFileProtocolExtraPrivileges fuse 来关闭这些超出传统浏览器的特权(详见 fuses.md)——它控制 file:// 页面能否用 fetch 加载其他 file:// 资产、能否使用 Service Worker 等。若你不从 file:// 服务页面,官方建议直接禁用该 fuse。
19. 检查可以变更哪些 fuses
Electron 自带一些"可能有用但大部分应用用不到"的选项。为避免为裁剪功能而自行编译维护一个 Electron fork,这些选项可以通过 Fuses 在打包时开/关。
为什么? 某些 fuse(如 runAsNode、nodeCliInspect)允许应用在被特定环境变量或 CLI 参数启动时表现出不同行为,可能被用来通过你的应用在设备上执行命令——让外部脚本运行它们原本无权、但你的应用可能有权执行的操作。
怎么做? @electron/fuses 就是为方便翻转 fuse 而制作的模块。用法与潜在错误场景参见该模块 README,以及我们的文档 How do I flip fuses?。
仓库中 docs/tutorial/fuses.md 完整列出了当前全部 fuse 及其默认值,例如:
| Fuse | 默认 | 说明 |
|---|---|---|
runAsNode |
启用 | 是否尊重 ELECTRON_RUN_AS_NODE 环境变量;关闭可防御一类 "living off the land" 攻击 |
cookieEncryption |
禁用 | 是否用 OS 级密钥加密磁盘上的 cookie 存储(注意为单向迁移) |
nodeOptions |
启用 | 是否尊重 NODE_OPTIONS / NODE_EXTRA_CA_CERTS |
nodeCliInspect |
启用 | 是否尊重 --inspect 等调试参数及 SIGUSR1 信号 |
embeddedAsarIntegrityValidation |
禁用 | 加载 app.asar 时校验其内容(macOS/Windows) |
onlyLoadAppFromAsar |
禁用 | 只允许从 app.asar 加载应用代码,配合上者可杜绝加载未校验代码 |
grantFileProtocolExtraPrivileges |
启用 | 是否给 file:// 页面超出传统浏览器的特权 |
推荐的安全基线:大多数应用可安全禁用 runAsNode、nodeOptions、nodeCliInspect,并启用 embeddedAsarIntegrityValidation、onlyLoadAppFromAsar、cookieEncryption。简单翻转方式:
const { flipFuses, FuseVersion, FuseV1Options } = require('@electron/fuses')
flipFuses(
// Electron 路径
require('electron'),
// 要翻转的 fuses
{
version: FuseVersion.V1,
[FuseV1Options.RunAsNode]: false
}
)
20. 不要向不可信 Web 内容暴露 Electron API
在预加载脚本中不应直接把 Electron API(尤其是 IPC)暴露给不可信 Web 内容。
为什么? 暴露 ipcRenderer.on 之类的裸 API 很危险:它让渲染进程直接接触整个 IPC 事件系统,从而能监听任何 IPC 事件,而不只是为它准备的那几个。我们也不能把回调直接传过去——因为 IPC 事件回调的第一个参数是 IpcRendererEvent 对象,其中包含 sender 等可触及底层 ipcRenderer 实例的属性;即使你只监听特定事件,直接传回调也意味着渲染器拿到了这个事件对象。简言之:不可信 Web 内容只应获得必要的信息与 API。
怎么做?
// 错误
contextBridge.exposeInMainWorld('electronAPI', {
on: ipcRenderer.on
})
// 也错误(回调被直接传入,事件对象泄漏)
contextBridge.exposeInMainWorld('electronAPI', {
onUpdateCounter: (callback) => ipcRenderer.on('update-counter', callback)
})
// 正确(在预加载侧剥离事件对象,只回传 value)
contextBridge.exposeInMainWorld('electronAPI', {
onUpdateCounter: (callback) => ipcRenderer.on('update-counter', (_event, value) => callback(value))
})
想深入了解
contextIsolation以及如何用它加固应用,参见 Context Isolation。
从源码看:这些安全默认值如何被"落地"
纵观全篇清单,很多建议其实已被 Electron 做成了默认值。这一点在主进程 WebPreferences 解析处有清晰的源码佐证:在 shell/browser/web_contents_preferences.cc 的构造函数中,node_integration_、node_integration_in_worker_ 被初始化为 false,而 context_isolation_ 初始化为 true——它们共同构成了"渲染进程默认隔离"的基石。当这些偏好被同步给渲染进程时(同文件第 399-404 行),nodeIntegration、nodeIntegrationInWorker、contextIsolation 等键值被写入进程偏好字典,驱动 Chromium 层真正生效。
把源码结论与文档对照,可以归纳出三条"默认值即安全"的演进主线,这也是新项目应当遵循的最低起点:
- Electron 5:默认关闭 Node 集成(对应第 2 条);
- Electron 12:默认开启上下文隔离(对应第 3 条);
- Electron 20:渲染进程默认开启沙箱(对应第 4 条)。
安全不是一次性动作,而是贯穿"框架升级 → 依赖审计 → 代码实现 → 运行配置 → 打包裁剪"全生命周期的持续实践。把官方这份 20 条清单当作应用发布前的强制门禁,配合控制台的 Electron Security Warning 逐项自检,你的 Electron 应用才能在保留桌面级能力的同时,不把用户暴露在 RCE 的悬崖边缘。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0626
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00