Electron Fuses
包特性切换
什么是 fuses ?
从安全角度上看,禁用某些未使用的 Electron 特性是明智之举,这些特性虽然功能强大,但有可能削弱应用程序的安全防护能力。 例如,任何不使用 ELECTRON_RUN_AS_NODE 环境变量的应用都应该禁用这一特性以防范一部分“离地”攻击。
我们也不想让 Electron 用户通过分叉来实现这一目标,因为从源代码开始构建以及维护分叉代码都是巨大的技术挑战,会消耗大量的时间和金钱。
Fuse 正是这一问题的解决方案。 从高层级来看,它们是 Electron 二进制文件里的“魔法位”,在打包 Electron 应用时可以被翻转以启用或禁用特定的特性/限制。
因为它们是在打包时被翻转的,之后就要对应用进行代码签名,所以操作系统就得负责通过系统级代码签名验证来确保这些位不会被翻转回去(比如 macOS 的 Gatekeeper 或者 Windows 的 AppLocker)。
当前可用的 Fuse
runAsNode
**默认值:**启用
@electron/fuses:FuseV1Options.RunAsNode
runAsNode Fuse 用于切换是否遵循 ELECTRON_RUN_AS_NODE 环境变量。 禁用了这个 Fuse 后,主进程里的 child_process.fork 将无法正常工作,因为它依赖这个环境变量才能运作。 我们推荐你使用实用进程来取代它,其适用于许多需要独立 Node.js 进程的使用场景(比如 SQLite 的服务器进程)。
cookieEncryption
**默认值:**禁用
@electron/fuses:FuseV1Options.EnableCookieEncryption
cookieEncryption Fuse 用于切换硬盘上存储的 Cookie 是否使用系统级加密密钥进行加密。 默认情况下,Chromium 用来存储 Cookie 的 SQLite 数据库是用明文形式来存储 Cookie 的值的。 如果你想确保你的应用的 Cookie 被加密,就如同 Chrome 所做的一样,那么你就应该启用这个 Fuse。 请注意这是单向转换—如果你启用这个 Fuse,已有的未加密 Cookie 将会在写入时被加密,但如果在此之后禁用这个 Fuse,那就会使你的 Cookie 存储损坏,无法读取。 大部分应用都可以安全地启用这个 Fuse。
nodeOptions
**默认值:**启用
@electron/fuses:FuseV1Options.EnableNodeOptionsEnvironmentVariable
nodeOptions Fuse 用于切换是否遵循 NODE_OPTIONS 环境变量和 NODE_EXTRA_CA_CERTS 环境变量。 NODE_OPTIONS 环境变量可用于向 Node.js 运行时传递各种自定义选项,通常生产环境下的应用不会用到这个变量。 大部分应用都可以安全地禁用这个 Fuse。
nodeCliInspect
**默认值:**启用
@electron/fuses:FuseV1Options.EnableNodeCliInspectArguments
nodeCliInspect Fuse 用于切换是否遵循 --inspect,--inspect-brk 等标志。 禁用后,它还会确保 SIGUSR1 信号不会初始化主进程检查器。 大部分应用都可以安全地禁用这个 Fuse。
embeddedAsarIntegrityValidation
**默认值:**禁用
@electron/fuses:FuseV1Options.EnableEmbeddedAsarIntegrityValidation
embeddedAsarIntegrityValidation Fuse 用于切换在 macOS 和 Windows 上,加载 app.asar 时是否验证其内容。 该特性旨在将性能影响降至最低,但仍有可能略微降低从 app.asar 归档文件内部读取文件的速度。 大部分应用都可以安全地启用这个 Fuse。
关于如何使用 ASAR 完整性验证的更多信息,请参阅 ASAR 完整性文档。
onlyLoadAppFromAsar
**默认值:**禁用
@electron/fuses:FuseV1Options.OnlyLoadAppFromAsar
The onlyLoadAppFromAsar fuse changes the search system that Electron uses to locate your app code. By default, Electron will search for this code in the following order:
app.asarappdefault_app.asar
When this fuse is enabled, Electron will only search for app.asar. When combined with the embeddedAsarIntegrityValidation fuse, this fuse ensures that it is impossible to load non-validated code.
loadBrowserProcessSpecificV8Snapshot
**默认值:**禁用
@electron/fuses: FuseV1Options.LoadBrowserProcessSpecificV8Snapshot
V8 snapshots can be useful to improve app startup performance. V8 lets you take snapshots of initialized heaps and then load them back in to avoid the cost of initializing the heap.
The loadBrowserProcessSpecificV8Snapshot fuse changes which V8 snapshot file is used for the browser process. By default, Electron's processes will all use the same V8 snapshot file. When this fuse is enabled, the main process uses the file called browser_v8_context_snapshot.bin for its V8 snapshot. Other processes will use the V8 snapshot file that they normally do.
Using separate snapshots for renderer processes and the main process can improve security, especially to make sure that the renderer doesn't use a snapshot with nodeIntegration enabled. See electron/electron#35170 for details.
grantFileProtocolExtraPrivileges
**默认值:**启用
@electron/fuses: FuseV1Options.GrantFileProtocolExtraPrivileges
The grantFileProtocolExtraPrivileges fuse changes whether pages loaded from the file:// protocol are given privileges beyond what they would receive in a traditional web browser. This behavior was core to Electron apps in original versions of Electron, but is no longer required as apps should be serving local files from custom protocols now instead.
If you aren't serving pages from file://, you should disable this fuse.
The extra privileges granted to the file:// protocol by this fuse are incompletely documented below:
file://protocol pages can usefetchto load other assets overfile://file://protocol pages can use service workersfile://protocol pages have universal access granted to child frames also running onfile://protocols regardless of sandbox settings
wasmTrapHandlers
**默认值:**启用
@electron/fuses: FuseV1Options.WasmTrapHandlers
The wasmTrapHandlers fuse controls whether V8 will use signal handlers to trap Out of Bounds memory access from WebAssembly. The feature works by surrounding the WebAssembly memory with large guard regions and then installing a signal handler that traps attempt to access memory in the guard region. The feature is only supported on the following 64-bit systems.
Linux. MacOS, Windows - x86_64 Linux, MacOS - aarch64
| Guard Pages | WASM heap | Guard Pages | |-----8GB-----| |-----8GB-----|
When the fuse is disabled V8 will use explicit bound checks in the generated WebAssembly code to ensure memory safety. However, this method has some downsides
- The compiler generates extra nodes for each memory reference, leading to longer compile times due to the additional processing time needed for these nodes.
- In turn, these extra nodes lead to lots of extra code being generated, making WebAssembly modules bigger than they ideally should be.
- This extra code, particularly the compare and branch before every memory reference, incurs a significant runtime cost.
How do I flip fuses?
简易方式
@electron/fuses is a JavaScript utility designed to make flipping these fuses easy. Check out the README of that module for more details on usage and potential error cases.
const { flipFuses, FuseVersion, FuseV1Options } = require('@electron/fuses')
flipFuses(
// Path to electron
require('electron'),
// Fuses to flip
{
version: FuseVersion.V1,
[FuseV1Options.RunAsNode]: false
}
)
You can validate the fuses that have been flipped or check the fuse status of an arbitrary Electron app using the @electron/fuses CLI.
npx @electron/fuses read --app /Applications/Foo.app
[!NOTE] If you are using Electron Forge to distribute your application, you can flip fuses using
@electron-forge/plugin-fuses, which comes pre-installed with all templates.
复杂方式
[!IMPORTANT] Glossary:
- Fuse Wire: A sequence of bytes in the Electron binary used to control the fuses
- Sentinel: A static known sequence of bytes you can use to locate the fuse wire
- Fuse Schema: The format/allowed values for the fuse wire
Manually flipping fuses requires editing the Electron binary and modifying the fuse wire to be the sequence of bytes that represent the state of the fuses you want.
Somewhere in the Electron binary, there will be a sequence of bytes that look like this:
| ...binary | sentinel_bytes | fuse_version | fuse_wire_length | fuse_wire | ...binary |
sentinel_bytesis always this exact string:dL7pKGdnNz796PbbjQWNKmHXBZaB9tsXfuse_versionis a single byte whose unsigned integer value represents the version of the fuse schemafuse_wire_lengthis a single byte whose unsigned integer value represents the number of fuses in the following fuse wirefuse_wireis a sequence of N bytes, each byte represents a single fuse and its state.- "0" (0x30) indicates the fuse is disabled
- "1" (0x31) indicates the fuse is enabled
- "r" (0x72) indicates the fuse has been removed and changing the byte to either 1 or 0 will have no effect.
To flip a fuse, you find its position in the fuse wire and change it to "0" or "1" depending on the state you'd like.
You can view the current schema here.