Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions 0005-change-fuse-defaults.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
- Start Date: 2024-06-24
- RFC PR: [electron/rfcs#0000](https://github.com/electron/rfcs/pull/6)
- Status: **Proposed**

# Change Electron Fuse Security Defaults

## Summary

We'd like to change the current default values for certain fuses exposed via [Electron Fuses](https://www.electronjs.org/docs/latest/tutorial/fuses).

## Motivation

The motivation for all of these swaps (outlined in details below) is entirely security focused. Ensuring that fuses are in the "hardened" state by default will protect apps, limit the amount of work that developers need to do to make secure apps and overall improve ths security posture of the Electron ecosystem.

## Guide-level explanation

The fuses that should have their defaults changed are listed below, along with the theoretical impact of the change, and proposed timelines.

### [`runAsNode`](https://www.electronjs.org/docs/latest/tutorial/fuses#runasnode)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think runAsNode is useful for running unit tests. Should we have this one behave differently for packaged/unpackaged like nodeCliInspect?


Currently defaults to enabled, we should switch this to default to disabled.

This flag makes it so that `ELECTRON_RUN_AS_NODE=1` does not cause your Electron app to become a floating node executable. The docs propose safer alternatives, primarily `utilityProcess.fork`.

We should deprecate (log a deprecation warning when `ELECTRON_RUN_AS_NODE` is used) and then swap the default. Proposed timeline is:
* Deprecate in Electron 43
* Swap default in Electron 45

### [`cookieEncryption`](https://www.electronjs.org/docs/latest/tutorial/fuses#cookieencryption)

Current defaults to disabled, we should switch this to default to enabled.

This flag enables encryption of cookies using user/device specific encryption keys. This is industry standard for cookie storage and only has one drawback, namely that it is a one way transition. Once you have run a version of Electron that has cookie encryption enabled, downgrading is a destructive operation.

We should swap the default without deprecation (it is unclear how to log a deprecation warning in a targeted way) but call it out at the top of the release notes + blog post. Proposed timeline is:
* Swap default in Electron 43

### [`nodeOptions`](https://www.electronjs.org/docs/latest/tutorial/fuses#nodeoptions)

Current defaults to enabled, we should switch this to default to disabled.

`NODE_OPTIONS` and `NODE_EXTRA_CA_CERTS` are basically unusable by default in Electron apps anyway due to how apps are traditionally launched. (They don't get environment variables by default on macOS for instance).

We should deprecate (log a deprecation warning when one of the env vars is used) and then swap the default. Proposed timeline is:
* Deprecate in Electron 43
* Swap default in Electron 45

### [`nodeCliInspect`](https://www.electronjs.org/docs/latest/tutorial/fuses#nodecliinspect)

Current defaults to enabled, we should switch this to default to disabled **when packaged**

This requires adding a new fuse mode, technical implementation withstanding we will need a way to indicate that a fuse is either:
* Enabled in dev and disabled when packaged
* Disabled in dev and enabled when packaged

Given the current fuse schema this should be easily expandable as each fuse has a whole byte of space to store information (currently just using 0/1 but have a full byte).

We should swap the default without deprecation (it is unclear how to log a deprecation warning in a targeted way) but call it out at the top of the release notes + blog post. Proposed timeline is:
* Swap default in Electron 43

### Other fuses of interest

Other security relevant fuses such as `grantFileProtocolExtraPrivileges`, `embeddedAsarIntegrityValidation` and `onlyLoadAppFromAsar` aren't included here for their own reasons.

`grantFileProtocolExtraPrivileges` is a new fuse and has not been tested enough or have a good enough disabled story for your average Electron app to change the default at this time. This can be revisited at some point in the future.

`embeddedAsarIntegrityValidation` and `onlyLoadAppFromAsar` could be enabled by default easily in packaging tools. But enabling by default in Electron itself is quite violent and would require substantial discussion, more-so than the fuses mentioned in this existing proposal and as such will be left to a later date. Ideally asar integrity is enabled by default on macOS and Windows at some point.

## Reference-level explanation

Technical implementation here is super easy, defaults are swapped in [`fuses.json5`](https://github.com/electron/electron/blob/main/build/fuses/fuses.json5)

## Drawbacks

These are breaking changes, despite deprecation warnings and release notes this may negative impact apps that upgrade and don't realize. The breakages should be fairly obvious to apps relying on these features though and therefore shouldn't slip through even basic testing.

## Rationale and alternatives

Only alternative is that we're happy with the status quo for all / a subset of these fuses.

## Prior art

N/A

## Unresolved questions

N/A
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rewording from a Slack message I made in #wg-releases back in February when these changes were first discussed. I've been intending to create an RFC describing this possible implementation, but haven't had the time. I'd still like to raise these concerns:

Historically the defaults for fuses have been permissive, and they’ve been flipped to more restrictive at packaging time. With the discussed changing of these defaults, this will no longer be true, and I don’t think we have a good DX for how app developers flip it back to true in development if their app needs that functionality.

Additionally, some fuses make sense to only flip at packaging time (onlyLoadAppFromAsar, nodeCliInspect, etc) but I think some would be beneficial to also flip in dev mode so that you have better consistency between dev and prod (runAsNode, grantFileProtocolExtraPrivileges, etc) and don’t get surprised by behavior differences.

Basically, I think it should be more straight forward for app developers to flip fuses in development. It’s technically possible at the moment by rolling your own using the @electron/fuses CLI, but it would be awkward DX and I don’t think we should push that to app developers to figure out if we could just make it supported functionality.

One possible implementation, for the sake of further illustration (would be fleshed out in a separate RFC):

  • Conceptually support a config.@electron/fuses key in package.json which defines boolean values for fuses, similar to how we have config.forge for Forge
  • Extend @electron/fuses CLI to add a new command (and expose it programmatically as well), say @electron/fuses apply which would read the config out of your app’s package.json and apply it to the Electron executable in node_modules
    • On first run it would create node_modules/electron/original_fuses.json (similar to how we dump the executable path into path.txt) so that the default state for any given fuse can always be restored by the CLI
  • Update npm/cli.js in the e/e repo so that before spawning Electron we apply fuses from the config (or reset them back to defaults if the developer has removed the config)

We could then also leverage config.@electron/fuses in Fiddle to support flipping fuses there. When uploading a fiddle to a Gist it would include that config and Fiddle could read it back on load and flip the fuses accordingly (requires some @electron/fiddle-core changes to ensure flipped fuses don’t bleed between runs).


## Future possibilities

N/A
Loading