Electron Fuses
包时间功能开关
fuses 是什么?
🌐 What are fuses?
从安全角度来看,禁用某些未使用的 Electron 功能是合理的,这些功能虽然强大,但可能会削弱你的应用的安全性。例如,任何不使用 ELECTRON_RUN_AS_NODE 环境变量的应用都希望禁用该功能,以防止一部分“借助现有系统资源的攻击”。
🌐 From a security perspective, it makes sense to disable certain unused Electron features
that are powerful but may make your app's security posture weaker. For example, any app that doesn't
use the ELECTRON_RUN_AS_NODE environment variable would want to disable the feature to prevent a
subset of "living off the land" attacks.
我们也不希望 Electron 的用户为了实现这个目标而进行分叉,因为从源码构建并维护分叉是一个巨大的技术挑战,并且耗费大量时间和金钱。
🌐 We also don't want Electron consumers forking to achieve this goal, as building from source and maintaining a fork is a massive technical challenge and costs a lot of time and money.
保险丝是解决这个问题的方法。从高层次来看,它们是 Electron 二进制文件中的“神奇位”,在打包你的 Electron 应用时可以翻转这些位,以启用或禁用某些功能/限制。
🌐 Fuses are the solution to this problem. At a high level, they are "magic bits" in the Electron binary that can be flipped when packaging your Electron app to enable or disable certain features/restrictions.
因为它们在打包时就已经被反转,然后你对应用进行代码签名,所以操作系统负责确保这些位不会通过操作系统级别的代码签名验证(例如 macOS 上的 Gatekeeper 或 Windows 上的 AppLocker)被反转回来。
🌐 Because they are flipped at package time before you code sign your app, the OS becomes responsible for ensuring those bits aren't flipped back via OS-level code signing validation (e.g. Gatekeeper on macOS or AppLocker on Windows).
当前熔丝位
🌐 Current fuses
runAsNode
默认: 启用
@electron/fuses: FuseV1Options.RunAsNode
runAsNode 熔断开关用于切换是否尊重 ELECTRON_RUN_AS_NODE 环境变量。禁用此熔断开关后,主进程中的 child_process.fork 将无法按预期工作,因为它依赖该环境变量才能运行。我们建议你使用 实用进程,它适用于许多需要独立 Node.js 进程的用例(例如 SQLite 服务器进程)。
🌐 The runAsNode fuse toggles whether the ELECTRON_RUN_AS_NODE
environment variable is respected or not. With this fuse disabled, child_process.fork in the main process will not function
as expected, as it depends on this environment variable to function. Instead, we recommend that you
use Utility Processes, which work for many use cases where you need a
standalone Node.js process (e.g. a SQLite server process).
cookieEncryption
默认: 已禁用
@electron/fuses: FuseV1Options.EnableCookieEncryption
cookieEncryption 熔丝用于切换磁盘上的 cookie 存储是否使用操作系统级别的加密密钥进行加密。默认情况下,Chromium 用来存储 cookie 的 SQLite 数据库存储的是明文值。如果你希望确保应用的 cookie 以与 Chrome 相同的方式加密,那么你应该启用此熔丝。请注意,这是一次性的转换——如果你启用此熔丝,现有的未加密 cookie 将在写入时加密,但随后如果禁用熔丝,会导致你的 cookie 存储损坏且无法使用。大多数应用可以安全地启用此熔丝。
🌐 The cookieEncryption fuse toggles whether the cookie store on disk is encrypted using OS level
cryptography keys. By default, the SQLite database that Chromium uses to store cookies stores the
values in plaintext. If you wish to ensure your app's cookies are encrypted in the same way Chrome
does, then you should enable this fuse. Please note it is a one-way transition—if you enable this
fuse, existing unencrypted cookies will be encrypted-on-write, but subsequently disabling the fuse
later will make your cookie store corrupt and useless. Most apps can safely enable this fuse.
nodeOptions
默认: 启用
@electron/fuses: FuseV1Options.EnableNodeOptionsEnvironmentVariable
nodeOptions 熔断开关用于切换是否遵循 NODE_OPTIONS 和 NODE_EXTRA_CA_CERTS 环境变量。NODE_OPTIONS 环境变量可以用来传递各种自定义选项给 Node.js 运行时,但在生产环境中应用通常不会使用它。大多数应用可以安全地禁用这个熔断开关。
🌐 The nodeOptions fuse toggles whether the NODE_OPTIONS
and NODE_EXTRA_CA_CERTS
environment variables are respected. The NODE_OPTIONS environment variable can be used to pass all
kinds of custom options to the Node.js runtime and isn't typically used by apps in production.
Most apps can safely disable this fuse.
nodeCliInspect
默认: 启用
@electron/fuses: FuseV1Options.EnableNodeCliInspectArguments
nodeCliInspect 保险丝用于切换是否尊重 --inspect、--inspect-brk 等标志。禁用时,它还确保 SIGUSR1 信号不会初始化主进程检测器。大多数应用可以安全地禁用此保险丝。
🌐 The nodeCliInspect fuse toggles whether the --inspect, --inspect-brk, etc. flags are respected
or not. When disabled, it also ensures that SIGUSR1 signal does not initialize the main process
inspector. Most apps can safely disable this fuse.
embeddedAsarIntegrityValidation
默认: 已禁用
@electron/fuses: FuseV1Options.EnableEmbeddedAsarIntegrityValidation
embeddedAsarIntegrityValidation 熔丝可以切换 macOS 和 Windows 上的一个功能,该功能在加载 app.asar 文件时会验证其内容。此功能设计为对性能影响最小,但可能会略微降低从 app.asar 压缩包中读取文件的速度。大多数应用可以安全地启用此熔丝。
🌐 The embeddedAsarIntegrityValidation fuse toggles a feature on macOS and Windows that validates the
content of the app.asar file when it is loaded. This feature is designed to have a minimal
performance impact but may marginally slow down file reads from inside the app.asar archive.
Most apps can safely enable this fuse.
欲了解有关如何使用 ASAR 完整性验证的更多信息,请查阅 Asar 完整性 文档。
🌐 For more information on how to use ASAR integrity validation, please read the Asar Integrity documentation.
onlyLoadAppFromAsar
默认: 已禁用
@electron/fuses: FuseV1Options.OnlyLoadAppFromAsar
onlyLoadAppFromAsar 熔断器会更改 Electron 用于定位你的应用代码的搜索系统。默认情况下,Electron 会按以下顺序搜索这些代码:
🌐 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
启用此保险丝时,Electron 将 只 会搜索 app.asar。当与 embeddedAsarIntegrityValidation 保险丝结合使用时,此保险丝可确保无法加载未经验证的代码。
🌐 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 快照可以用来提高应用启动性能。V8 允许你对已初始化的堆进行快照,然后再加载它们,以避免初始化堆的开销。
🌐 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.
loadBrowserProcessSpecificV8Snapshot 保险丝会更改浏览器进程使用的 V8 快照文件。默认情况下,Electron 的所有进程都会使用相同的 V8 快照文件。当启用此保险丝时,主进程将使用名为 browser_v8_context_snapshot.bin 的文件作为其 V8 快照。其他进程将使用它们通常使用的 V8 快照文件。
🌐 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.
为渲染进程和主进程使用独立的快照可以提高安全性,尤其是为了确保渲染进程不会使用启用了 nodeIntegration 的快照。有关详细信息,请参见 electron/electron#35170。
🌐 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
grantFileProtocolExtraPrivileges 保险丝决定了通过 file:// 协议加载的页面是否会获得超出传统网页浏览器所授予的权限。这种行为在早期版本的 Electron 应用中是核心特性,但现在已经不再需要,因为应用现在应该通过自定义协议提供本地文件。
🌐 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.
如果你没有从 file:// 提供页面,你应该禁用这个保险丝。
🌐 If you aren't serving pages from file://, you should disable this fuse.
此保险丝授予 file:// 协议的额外权限在下面的文档中记录不完整:
🌐 The extra privileges granted to the file:// protocol by this fuse are incompletely documented below:
file://协议页面可以使用fetch通过file://加载其他资源file://协议页面可以使用服务工作者file://协议页面对同样运行file://协议的子框架也授予了通用访问权限,无论沙盒设置如何
如何切换熔丝位?
🌐 How do I flip fuses?
最简单的方法
🌐 The easy way
@electron/fuses 是一个 JavaScript 工具,用于轻松切换这些熔断器。有关使用方法和可能出现的错误情况,请查看该模块的 README。
const { flipFuses, FuseVersion, FuseV1Options } = require('@electron/fuses')
flipFuses(
// Path to electron
require('electron'),
// Fuses to flip
{
version: FuseVersion.V1,
[FuseV1Options.RunAsNode]: false
}
)
你可以验证已经切换的保险丝,或者使用 @electron/fuses CLI 检查任意 Electron 应用的保险丝状态。
🌐 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
[!注意] 如果你正在使用 Electron Forge 来分发你的应用,你可以使用
@electron-forge/plugin-fuses来切换熔断器,该工具在所有模板中都预装了。
艰难的路
🌐 The hard way
术语表:
- 保险丝线:Electron 二进制文件中用于控制保险丝的一系列字节
- 哨兵:可以用来定位保险丝线的已知静态字节序列
- 保险丝格式:保险丝线的格式/允许的值
手动切换保险丝需要编辑 Electron 二进制文件,并将保险丝线路修改为表示所需保险丝状态的字节序列。
🌐 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.
在 Electron 二进制文件的某个位置,会有一段类似这样的字节序列:
🌐 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_bytes总是这个确切的字符串:dL7pKGdnNz796PbbjQWNKmHXBZaB9tsXfuse_version是一个单字节,其无符号整数值表示熔丝模式的版本fuse_wire_length是一个单字节,其无符号整数值表示以下保险丝丝中的保险丝数量fuse_wire是一个由 N 个字节组成的序列,每个字节代表一个保险丝及其状态。- "0" (0x30) 表示保险丝已禁用
- "1" (0x31) 表示保险丝已启用
- “r”(0x72)表示保险丝已被移除,将字节更改为 1 或 0 都不会有影响。
要切换保险丝,你需要找到其在保险丝线中的位置,然后根据你希望的状态将其更改为“0”或“1”。
🌐 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.