メインコンテンツへ飛ぶ

Electron Fuse

パッケージ時機能切り替え

Fuse とはなんでしょうか?

Electron は機能の集合体なので、アプリケーション全体に渡って特定の機能を無効化しても合理的です。 例えば、99% のアプリは ELECTRON_RUN_AS_NODE を利用しないので、そういったアプリでその機能が利用できないバイナリを頒布できるようにしたいのです。 Electron の消費者がソースから Electron を構築することは技術的に大きな障害であり時間とお金両方のコストがかかるため、それも避けたいと考えています。

Fuse はこの問題の解決策です。高水準としては Electron バイナリ内の "マジックビット" であり、Electron アプリをパッケージングする際にそれらを反転させることで、特定の機能や制限を有効化/無効化できます。 アプリのコード署名前のパッケージ時に反転するので、OS は OS レベルのコード署名検証(Gatekeeper / App Locker) の時に反転しないようにする責任があります。

現在の Fuse

runAsNode

デフォルト: 有効

@electron/fuses: FuseV1Options.RunAsNode

runAsNode の Fuse は、 ELECTRON_RUN_AS_NODE 環境変数を尊重するかどうかを切り替えます。 この Fuse が無効になっている場合の注意として、メインプロセスの process.fork はこの環境変数に依存して動作するため、期待通りに機能しないことがあります。 代わりに、ユーティリティプロセス を使用することを推奨します。これは、スタンドアロンの Node.js プロセス (SQLite サーバーのプロセスや同様の場合) が必要な多くのユースケースで機能します。

cookieEncryption

デフォルト: 無効

@electron/fuses: FuseV1Options.EnableCookieEncryption

cookieEncryption の Fuse は、ディスクに保存する Cookie を OS レベルの暗号化キーで暗号化するかどうかを切り替えます。 デフォルトでは、Chromium が Cookie を保存に使用する SQLite データベースは値を平文で保存します。 Chrome と同じくアプリの Cookie を暗号化するようにしたい場合は、この Fuse を有効にすべきです。 これは一方向の変更です。この Fuse を有効にすると、暗号化されていないクッキーは書き込み時に暗号化されます。しかしその後再びこの Fuse を無効にすると、Cookie ストアが事実上破損して使い物にならなくなりますので、ご注意ください。 ほとんどのアプリは安全にこの Fuse を有効化できます。

nodeOptions

デフォルト: 有効

@electron/fuses: FuseV1Options.EnableNodeOptionsEnvironmentVariable

nodeOptions の Fuse は NODE_OPTIONSNODE_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 の実験的機能である、読み込み時に app.asar ファイルの内容を検証する機能を切り替えます。 この機能はパフォーマンスへの影響を最小限に抑えるように設計されていますが、 app.asar アーカイブ内からのファイル読み込みがわずかに遅くなる可能性があります。

For more information on how to use asar integrity validation please read the Asar Integrity documentation.

onlyLoadAppFromAsar

デフォルト: 無効

@electron/fuses: FuseV1Options.OnlyLoadAppFromAsar

onlyLoadAppFromAsar Fuse は、Electron がアプリコードの探索に使用する探索システムを変更します。 デフォルトでは Electron は app.asar -> app -> default_app.asar の順番で探索します。 この Fuse が有効な場合、探索順序は 1 つのエントリ app.asar になるので、embeddedAsarIntegrityValidation Fuse と組み合わせたときに非検証コードを読み込めないようにします。

loadBrowserProcessSpecificV8Snapshot

デフォルト: 無効

@electron/fuses: FuseV1Options.LoadBrowserProcessSpecificV8Snapshot

loadBrowserProcessSpecificV8Snapshot Fuse は、ブラウザープロセスに使用する V8 スナップショットファイルを変更します。 デフォルトでは、Electron のプロセスはすべて同じ V8 スナップショットファイルを使用します。 この Fuse が有効な場合、ブラウザープロセスは browser_v8_context_snapshot.bin というファイルを 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.

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 #35170 for details.

grantFileProtocolExtraPrivileges

デフォルト: 有効

@electron/fuses: FuseV1Options.GrantFileProtocolExtraPrivileges

grantFileProtocolExtraPrivileges Fuse は、file:// プロトコルから読み込まれたページに、従来のウェブブラウザが受ける権限を超えるような権限を付与するかどうかを変更します。 この動作はかつての Electron では Electron アプリの中核を担うものでしたが、現在はアプリが カスタムプロトコルからローカルファイルを提供する 必要があるため、もはや必要ありません。 file:// からページを提供していない場合は、この Fuse を無効にすべきです。

この Fuse によって file:// プロトコルに付与される追加の権限は、大雑把にまとめると以下のようになります。

  • file:// プロトコルのページが fetch を使用して file:// 越しにその他のアセットを読み込めます
  • file:// プロトコルのページがサービスワーカーを使用できます
  • file:// プロトコルのページが、サンドボックスの設定に関係なく file:// プロトコル上で動く子フレームにも普遍的なアクセス権限を持ちます

Fuse の反転方法は何ですか?

簡単な方法

これら Fuse を簡単に反転させるために、便利なモジュール @electron/fuses を作成しました。 使用方法や潜在的なエラーケースといった詳細は、このモジュールの README を確認してください。

const { flipFuses, FuseVersion, FuseV1Options } = require('@electron/fuses')

flipFuses(
// electron へのパス
require('electron'),
// 反転する Fuse
{
version: FuseVersion.V1,
[FuseV1Options.RunAsNode]: false
}
)

Fuses CLI を使用すると、Fuse が反転したことの検証や、任意の Electron アプリの Fuse の状態を確認できます。

npx @electron/fuses read --app /Applications/Foo.app

難しい方法

簡易用語集

  • Fuse Wire: Fuse の制御に使用する Electron バイナリ内のバイト列
  • Sentinel: Fuse Wire の位置特定に使用できる静的な既知のバイト列
  • Fuse Schema: Fuse Wire が許容する値の形式

手動で Fuse を反転させるには、Electron バイナリを編集し必要な Fuse の状態を表すバイト列になるように Fuse Wire を修正する必要があります。

Electron バイナリのどこかに、以下のようなバイト列があります。

| ...binary | sentinel_bytes | fuse_version | fuse_wire_length | fuse_wire | ...binary |
  • sentinel_bytes は厳密にこのような文字列 dL7pKGdnNz796PbbjQWNKmHXBZaB9tsX です
  • fuse_version は単一バイトで、その符号無し整数の値が Fuse Schema のバージョンを表します。
  • fuse_wire_length は単一バイトで、その符号無し整数の値は後続の Fuse Wire にある Fuse の数を表します。
  • fuse_wire は N バイトのシーケンスで、各バイトは 1 つの Fuse とその状態を表します。
    • "0" (0x30) は無効な Fuse を表します
    • "1" (0x31) は有効な Fuse を表します
    • "r" (0x72) は削除された Fuse を表し、このバイトを 1 や 0 に変更しても効果はありません。

Fuse を反転させるには、Fuse Wire の位置を見つけ、状態に応じて "0" または "1" に変更します。

現在のスキーマは こちら で閲覧できます。