Electron ASAR 完整性校验(ASAR Integrity)原理与接入指南
ASAR Integrity 是 Electron 提供的运行时安全校验机制:开启后,应用会在运行时对自身 app.asar 的内容与打包期写入的哈希进行一致性校验,一旦发现文件被篡改便直接强制终止进程,从而防止应用代码被静态修改后重新分发。本文以本仓库官方教程 docs/tutorial/asar-integrity.md 为骨架,结合 Electron 源码、fuse 体系与集成测试,系统讲解 ASAR Integrity 的版本支持、工作原理、fuse 开关、macOS/Windows 双平台的打包期哈希注入方法,以及校验失败时的真实表现。
ASAR Integrity 是什么
ASAR(Atom Shell Archive)是 Electron 用来打包应用源码的归档格式(格式说明见 asar-archives 教程)。普通 ASAR 归档没有防篡改能力:攻击者拿到应用包后可以解包、修改其中任意 JS 再重新打包,由于可执行文件并未对归档内容做校验,被篡改的代码照样会被加载执行。
ASAR Integrity 正是针对这一缺口设计的安全特性。它的核心思想是**“打包期计算哈希,运行期验证哈希”**:
- 在打包时,把整个 ASAR 头的哈希(header hash)嵌入到可执行文件的元数据里;
- 在运行时,Electron 校验 ASAR 头的哈希是否一致;同时在按需读取文件内容时,对照归档内嵌的逐文件/逐块哈希做校验;
- 若头哈希缺失或对不上、或读取的内容校验失败,应用会强制终止(forcefully terminate),而不是带着被篡改的数据继续运行。
按官方教程描述,该特性仍处于实验阶段(experimental),默认关闭,需要打包期显式开启。它针对的核心攻击面是对分发渠道上应用包的静态篡改,让篡改后的应用无法正常启动。
版本支持情况
| 平台 | 支持起始版本 |
|---|---|
| macOS | electron >= 16.0.0 |
| Windows | electron >= 30.0.0 |
除了 Electron 本身版本要满足要求之外,还需要打包 app.asar 时使用的 asar 工具支持生成完整性元数据:
asar@3.1.0首次引入了完整性支持;- 此后该 npm 包迁移更名为
@electron/asar,所有版本的@electron/asar均支持 ASAR integrity(当前仓库的 devDependency 即为@electron/asar@^4.3.0)。
关于 Mac App Store(MAS)构建
官方文档特别说明:ASAR integrity 在 MAS(Mac App Store)构建中完全支持,且被推荐作为最佳实践。需要理解的是:
- 通过 MAS 安装的应用,其
Resources/目录已被系统保护(目录属主为 root,普通进程无法写入),系统层面已有只读防护; - 即便如此,ASAR integrity 仍能提供额外一层安全纵深;
- 如果你使用 Electron 的 MAS 构建产物、却通过非 App Store 渠道分发(例如官网直接下载),这类安装不具备系统级只读保护,此时 ASAR integrity 尤为重要。
工作原理:两级哈希体系
理解 ASAR Integrity 需要区分“归档内部”与“归档外部”两级哈希:
归档头 JSON 中的 integrity 元数据
ASAR 文件本身是一段 Pickle 化的头部 + 文件数据区。每个 ASAR 归档内含一段 JSON 字符串形式的 header,header 中会包含 integrity 对象,其结构如下(取自官方教程示例):
{
"algorithm": "SHA256",
"hash": "...",
"blockSize": 1024,
"blocks": ["...", "..."]
}
字段含义:
algorithm:哈希算法标识,当前仅支持SHA256;hash:十六进制编码的完整内容哈希;blockSize:分块大小(单位:字节),即每个块包含多少个字节;blocks:十六进制编码的逐块哈希数组,数组中每个元素对应当前内容的一个blockSize大小的数据块。
需要说明的是:这个 JSON 示例是归档内“受保护数据段”的完整哈希 + 分块哈希结构。它的实际粒度由打包工具 @electron/asar 决定——从本仓库的解析代码看,archive 打开时会遍历 header 的 file table,逐节点读取其 integrity 字段(algorithm/hash/blockSize/blocks)并挂到对应条目的元数据上,见 shell/common/asar/archive.cc。因此 blockSize/blocks 的分块粒度通常是按归档内文件条目组织的:大文件可被切成多个块分别做哈希,从而支持“读到哪块验到哪块”的增量校验,避免一次性读取并哈希整个文件。仓库集成测试里构造的测试归档即采用 4MB(4 * 1024 * 1024)的块大小,并刻意在每块开头写入标记以便精准篡改某个块,参见 spec/asar-integrity-spec.ts。
打包期单独注入的“整个 ASAR 头”哈希
与上面的归档内元数据不同,你还必须在打包应用时另外定义一个对整个 ASAR header 的十六进制哈希(header hash),并把它写入应用的可执行文件(macOS 为 Info.plist,Windows 为 exe 资源,详见后文“提供 header hash”一节)。这个“外部”哈希是整个校验链路的第一道闸门:
- 运行期打开可被校验的 asar 归档时,Electron 先取出这个外部头哈希;
- 对归档实际读出的 header JSON 字节流计算 SHA256 并比对;
- 如果外部没有提供哈希、或者哈希不匹配,应用会直接强制终止。
对应的实现见 shell/common/asar/archive.cc:在 Archive::Init() 中,若归档需要校验(load_integrity 且外部提供了 integrity 配置),会先取 HeaderIntegrity(),拿不到就 LOG(FATAL) << "Failed to get integrity for validatable asar archive";随后对 header 字节调用 ValidateIntegrityOrDie,验证通过才把 header_validated_ 置真,此后 header 内嵌的逐文件哈希才被信任用于内容读取校验。
读取路径上的逐块校验
头哈希校验通过后,还有一层内容级校验:所有通过归档读出的数据都会对照条目自身哈希做验证。核心兜底函数是 shell/common/asar/asar_util.cc 中的 ValidateIntegrityOrDie:
void ValidateIntegrityOrDie(base::span<const uint8_t> input,
const IntegrityPayload& integrity,
std::string_view what) {
if (integrity.algorithm == HashAlgorithm::kSHA256) {
const std::string hex_hash =
base::ToLowerASCII(base::HexEncode(crypto::hash::Sha256(input)));
if (integrity.hash != hex_hash) {
LOG(FATAL) << "Integrity check failed for asar archive entry '" << what
<< "' ...";
}
} else {
LOG(FATAL) << "Unsupported hashing algorithm in ValidateIntegrityOrDie";
}
}
这里的设计要点:比较是对整个条目内容哈希做全量比对,失败即 FATAL。而对于流式读取(如渲染进程经 network service 读取文件数据),则通过 Mojo 的 FilteredDataSource::Filter 机制按块增量验证——对应的 AsarFileValidator 记录当前块与已累计哈希字节数,在每次读块结束时核对当前块的哈希,见 shell/browser/net/asar/asar_file_validator.h。archive.cc 中 FillFileInfoWithNode 读取条目 integrity、并携带 header_validated_ 标记,就是为了保证“只有头哈希被验证过的归档,其内嵌分块哈希才可用于内容校验”,见 shell/common/asar/archive.cc。
在可执行文件中开启:相关 Fuse
ASAR Integrity 校验默认关闭,需要在打包构建期通过切换 Electron fuse 来开启。fuse 是写在 Electron 二进制中的“魔法位”,打包期翻转、之后随应用一起代码签名,被签名保护后便无法在分发后被改回(系统层由 macOS Gatekeeper / Windows AppLocker 之类的签名校验机制兜底)。关于 fuse 的整体机制与全部开关,参见 fuses 文档。
涉及本特性的两个 fuse:
EnableEmbeddedAsarIntegrityValidation
默认:关闭
- @electron/fuses 选项:
FuseV1Options.EnableEmbeddedAsarIntegrityValidation - 作用:在 macOS 与 Windows 上,开启对
app.asar内容加载时的完整性校验。 - 性能影响:官方描述该特性“设计上追求最小性能影响”,但可能让从
app.asar内部读取文件的速度略有下降,因为读取多了一重哈希计算。
OnlyLoadAppFromAsar(强烈建议同时开启)
默认:关闭
- @electron/fuses 选项:
FuseV1Options.OnlyLoadAppFromAsar - 作用:默认情况下 Electron 按
app.asar→app→default_app.asar的顺序搜索应用代码;开启该 fuse 后,只允许从app.asar加载应用代码。
官方教程明确警告:开启完整性校验 fuse 时,通常应同时开启 onlyLoadAppFromAsar。否则,完整性校验可以通过“Electron 应用代码搜索路径”(即让它从未受校验的 app 目录加载代码)被绕过。两者结合才能保证“不可能加载未经校验的代码”。
开启示例(使用 @electron/fuses):
const { flipFuses, FuseVersion, FuseV1Options } = require('@electron/fuses')
flipFuses(
// E.g. /a/b/Foo.app(macOS 上指向 .app 包,Windows 上指向 exe)
pathToPackagedApp,
{
version: FuseVersion.V1,
[FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true,
[FuseV1Options.OnlyLoadAppFromAsar]: true
}
)
需要留意:flipFuses 会改写二进制,因此翻转 fuse 之后、正式发布之前必须重新做(代码)签名,否则 macOS 的 ad-hoc/正式签名失效;仓库集成测试在翻转前显式设置 resetAdHocDarwinSignature: true 以重新生成 ad-hoc 签名,见 spec/asar-integrity-spec.ts。最终由操作系统签名校验保证 fuse 位在分发后不可被反转。
提示:若使用 Electron Forge,可通过
@electron-forge/plugin-fuses插件在 Forge 配置文件中完成 fuse 配置(见 Forge fuses 插件说明)。
提供 header hash:打包期注入
完整性校验比对的对象,是打包时你提供的那个归档头哈希。提供方式因平台而异,且不同打包工具链的处理方式完全不同。
使用 Electron 官方工具链(推荐)
如果你使用 Electron Forge 或 Electron Packager,只要启用了 asar,它们会自动完成上述 macOS/Windows 的哈希注入,无需额外配置。使用本特性所需的最低版本为:
@electron/packager@18.3.1@electron/forge@7.4.0
使用其他构建系统:macOS
macOS 打包时,必须在产物应用的 Info.plist 中填充一个合法的 ElectronAsarIntegrity 字典块。示例:
<key>ElectronAsarIntegrity</key>
<dict>
<key>Resources/app.asar</key>
<dict>
<key>algorithm</key>
<string>SHA256</string>
<key>hash</key>
<string>9d1f61ea03c4bb62b4416387a521101b81151da0cfbe18c9f8c8b818c5cebfac</string>
</dict>
</dict>
说明:
- 合法的
algorithm取值目前仅有SHA256; - 外层 key(如
Resources/app.asar)是归档相对Contents/的相对路径; hash是用指定算法对该归档的 ASAR header 计算得到的哈希;- 生成方法:
@electron/asar包暴露了getRawHeader方法,对其返回结果的headerString做哈希即可。
对应到 Electron 源码侧:macOS 下 Archive::HeaderIntegrity() 从主 bundle 的 Info.plist 读取 ElectronAsarIntegrity 字典,用归档相对 Contents 的路径(RelativePath())查表,并校验 algorithm 必须为 SHA256 后才返回哈希载荷,见 shell/common/asar/archive_mac.mm。
借助 node:crypto 计算头哈希的参考脚本(与本仓库集成测试中的 headerHash 辅助函数一致,见 spec/asar-integrity-spec.ts):
const { createHash } = require('node:crypto')
const { getRawHeader } = require('@electron/asar')
const hash = createHash('sha256')
.update(getRawHeader('/path/to/app.asar').headerString)
.digest('hex')
// 将 hash 写入 Info.plist 的 ElectronAsarIntegrity 字典
使用其他构建系统:Windows
Windows 打包时,必须在可执行文件中填充一个合法的 资源(resource)条目:
- 资源类型(type):
Integrity - 资源名称(name):
ElectronAsar - 资源值: 一个 JSON 编码的字典数组,形式如下:
[
{
"file": "resources\\app.asar",
"alg": "sha256",
"value": "9d1f61ea03c4bb62b4416387a521101b81151da0cfbe18c9f8c8b818c5cebfac"
}
]
其中 file 为归档相对资源目录(assets 目录)的路径,alg 为小写算法名,value 为十六进制哈希值。
这一格式并非随意约定,它必须与 Electron 侧解析逻辑严格一致。Windows 端实现见 shell/common/asar/archive_win.cc:
- 通过 Win32 API
FindResource/LoadResource从当前模块(exe)中查找类型为Integrity、名称为ElectronAsar的资源; - 将资源内容作为 JSON 解析,要求顶层必须是数组,逐项校验字段
file(非空字符串)、alg(小写后必须等于sha256)、value(非空); - 校验通过后以“小写化后的文件路径”为 key 建缓存表,供
Archive::HeaderIntegrity()查询。若归档路径在表中找不到对应项,会直接LOG(FATAL)。
macOS 与 Windows 两条读取路径虽然平台 API 不同,但都归结为返回一个 IntegrityPayload(algorithm = HashAlgorithm::kSHA256 + 十六进制 hash),供 Archive::Init() 做头哈希比对。
官方文档给出的 Windows 实现参考是 Electron Packager 源码中的
src/resedit.ts(通过resedit等库改写 PE 资源)。仓库集成测试中也用resedit的NtExecutableResource以相同形状向 exe 写入Integrity/ElectronAsar资源,见 spec/asar-integrity-spec.ts。
校验失败时的真实行为:源码与测试佐证
官方教程的核心行为承诺是:若没有任何哈希、或哈希不匹配,应用将被强制终止。仓库的集成测试 spec/asar-integrity-spec.ts 对这一行为做了非常系统的验证,可归纳为以下几个层面:
1. fuse 开关本身决定是否生效
EnableEmbeddedAsarIntegrityValidation开启时:正常未篡改的归档可正常启动退出(退出码 0);- 篡改归档头后启动,进程崩溃(macOS 上表现为
SIGABRT/SIGTRAP,Windows 上为非零退出码),输出中包含Integrity check failed for asar archive; - 同一篡改在 fuse 关闭时完全不做任何校验,应用照常运行(退出码 0)。
这组对照实验直接证明了“fuse 是总开关”以及“运行期校验确实会以终止进程为代价”。
2. 不同加载路径上的篡改检测
测试分别篡改了不同语义的内容,并断言应用终止:
- 篡改主进程加载的 JS:进程以状态码 1 退出并打印
ASAR Integrity Violation: got a hash mismatch; - 篡改渲染进程读取的内容(如
require-trusted-types-for这类 renderer 资源):进程崩溃,输出包含Failed to validate block while ending ASAR file stream; - 篡改
fs.open一族的文件读取路径(测试覆盖fd、stream、handle、copy四种模式):全部无法读到原内容,以状态码 1 退出并报 hash mismatch,且输出中不会出现*-read-ok标记。
3. 多块条目的增量校验语义
对于包含多个 4MB 块的归档条目,测试进一步验证了块级校验的精确语义:
- 篡改第 N 块后流式读取:到达被篡改块之前的数据可以正常流出,一旦流推进到被篡改的块便终止,且被篡改块的任何一个字节都不会被交付;
- 当只有较靠后的块被篡改时,只读取完好块的区间请求(range read)依然成功——说明校验是按被实际读取的块粒度发生的,不影响与篡改块无关的读取;
- 整文件读取(
readFile)遇到含篡改块的文件则必然失败; - 甚至在“某块先被完整读取过、随后又被就地篡改、再次读取”的场景下,依然能检测到哈希不匹配——说明读取不会无条件信任此前的结果。
这些用例一方面印证了“读到哪块验到哪块”的增量设计(配合 asar_file_validator.h 的块级 Mojo Filter),另一方面也说明从主进程 fs API 到渲染进程网络读取的整条数据通路都被纳入了校验范围。
使用前提与注意点小结
在项目里落地 ASAR Integrity,建议按如下顺序检查:
- 确认平台与版本:macOS 需要 Electron ≥ 16,Windows 需要 Electron ≥ 30;若使用 MAS 构建并走非 App Store 分发,务必开启。
- 确认 asar 工具:使用支持 integrity 的
@electron/asar(asar@3.1.0起,其后所有版本均可)。 - 打包期开启 fuse:翻转
EnableEmbeddedAsarIntegrityValidation,并同时翻转OnlyLoadAppFromAsar以防绕过;翻转后重新签名。 - 注入头哈希:优先使用 Electron Packager(≥ 18.3.1)/ Electron Forge(≥ 7.4.0)自动完成;自定义构建系统时,macOS 写
Info.plist的ElectronAsarIntegrity字典(算法仅支持SHA256,哈希用getRawHeader(...).headerString经node:crypto生成),Windows 写入类型Integrity、名称ElectronAsar的 JSON 数组资源。 - 理解性能与失败模型:开启后从
app.asar内部读文件可能略有变慢;一旦头哈希缺失/不匹配或读取内容哈希不一致,Electron 会以 Fatal 方式终止应用,因此要保证打包流水线生成的哈希与最终归档严格对应,任何“打包后改 asar”的流程都会导致应用无法启动。
综上,ASAR Integrity 提供了一条从“明文归档可被任意篡改”到“归档头 + 文件内容双层哈希、篡改即终止”的加固路径,其全部机制都围绕 asar 归档格式、fuse 打包期开关 以及 archive 运行时校验 三块代码实现展开,适合作为 Electron 应用发布安全加固的首选方案之一。
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 StartedRust0623
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