Secure Automatic Updates for Electron Apps
Tags: security, node, javascript
Published: 2019-09-05 12:54:27

There are a lot of popular desktop applications today written in Javascript and HTML, thanks to frameworks like [Electron](https://electronjs.org). The most noteworthy example that comes to mind is [Streamlabs OBS](https://streamlabs.com/streamlabs-obs), which is popular among Twitch streamers.

A lot of these apps even include a self-update mechanism for ensuring users are always on a recent version of the software. However, self-updaters are a land mine (or a gold mine, depending on your perspective) of security risks.

However, [they're definitely worth the risk](https://dev.to/paragonie/the-case-for-automatic-updates-477a). It's just important to do them right.

## Understanding the Risks Inherent to Automatic Updates

In general, the best way to understand security risks is to **think like a bad guy**, then try to outsmart yourself.

![Soatok hacking](https://soatok.com/static/stickers/Soatok_STICKERPACK-HACKER.png)

If you wanted to install malware on thousands (or millions) of computers, and all of the targets you were interested in were running some software that has a self-update mechanism, wouldn't it make perfect sense to attack the update server and replace the update file with your malware?

This isn't just a theoretical risk. Both [download links](https://www.bleepingcomputer.com/news/security/hacker-compromised-official-phpbb-download-links) and [self-updaters](https://www.theregister.co.uk/2017/06/28/petya_notpetya_ransomware/) have historically been used to spread malware in the past.

Let's assume someone hacks into your update server and publishes a fake update for your app that contains their malware of choice. How can we stop them from infecting our users?

> Can we use cryptographic hash functions?

[**No!** Hash functions don't help us here.](https://dev.to/paragonie/you-wouldnt-base64-a-password-cryptography-terms-and-concepts-for-developers#download-verification)

There's a lot of "old school" ideas about download authenticity. The idea of "just verify hashes/checksums" doesn't work because *there are no secrets* the attacker cannot access.

> Isn't HTTPS (HTTP over TLS) enough?

TLS is good, and I would argue, *necessary* for solving this problem. But it is, in and of itself, inadequate.

As the name **T**ransport-**L**ayer **S**ecurity implies, TLS protects data in-transit. It provides no at-rest authenticity for the update file sitting on the server. If someone can hack the other endpoint, TLS doesn't help you.

![I know this can seem frustrating, bear with me please](https://soatok.com/static/stickers/Soatok_STICKERPACK-FACEPAW.png)

### What Actually Works?

Digital Signatures work!

![Eureka!](https://soatok.com/static/emotes/idea.png)

Digital Signatures are a class of asymmetric cryptography algorithms that compute a **signature** of a **message**, generated by a secret **signing key** (or "private key" in Academic Speak), which can be verified by a publicly known **verification key** (a.k.a. "public key").

Due to the nature of asymmetric cryptography, only your signing key needs to remain secret.

So what you have to do is:

1. Generate a digital signature of your update files, offline.
2. Upload the signature alongside your update files to the update server.

And viola! Now even if someone hacks into the update server, they cannot push malware onto your users without further attacks in order to steal your signing key. If you keep this key in a computer that is never connected to the Internet, stealing it becomes prohibitively expensive for most attackers.

But is a digital signature by itself adequate for developing a secure automatic update system?

The experts say, "No."

* [The Triangle of Secure Code Delivery](https://defuse.ca/triangle-of-secure-code-delivery.htm)
* [Guide to Automatic Security Updates (For PHP Developers)](https://paragonie.com/blog/2016/10/guide-automatic-security-updates-for-php-developers)
* [Proposal to secure Go's module ecosystem](https://go.googlesource.com/proposal/+/master/design/25530-sumdb.md)

That being said, digital signatures are a *fundamental* component to any effort to secure software updates. You cannot remove them from the equation without making the system less secure.

The full solution consists of each of the following:

1. Digital signatures
2. Reproducible builds
3. Binary transparency (a.k.a. Userbase Consistency Verification)
   * This uses cryptographic ledgers, but be wary of anything with "blockchain" in its sales brochure
4. Transport-Layer Security (to prevent Man-in-the-Middle replay attacks to keep targeted systems vulnerable forever)

That might sound daunting, but I didn't just write this post to talk about the theory of secure automatic updates with respect to Electron apps. Experts have already talked about the problems and solutions at length before.

Today, I'd like to introduce you to **my solution to the problem** (which was based off [the work done to secure WordPress's auto-updater](https://core.trac.wordpress.org/ticket/39309)).

# Project Valence

Project Valence (named after valence electrons) is my framework for self-updating Electron apps. It consists of three main projects.

1. [libvalence](https://github.com/soatok/libvalence) is the component you would add to an existing Electron.js project in order to facilitate secure updates
2. [valence-devtools](https://github.com/soatok/valence-devtools) is a `npm` package you'll want to install globally in order to package, sign, and release updates
3. [valence-updateserver](https://github.com/soatok/valence-updateserver) is a web application that exposes an API that the other two projects can communicate with in order to upload/download updates and signatures

The cryptography used by Valence is [Dhole cryptography](https://dholecrypto.com), an easy-to-use libsodium wrapper.

For signatures, Dhole uses Ed25519 (with an additional 256-bit random nonce to make [fault attacks](https://research.kudelskisecurity.com/2017/10/04/defeating-eddsa-with-faults/) more difficult if reimplemented in embedded systems).

## Valence Update Server

The [install/setup instructions](https://github.com/soatok/valence-updateserver#install--setup-instructions) are available on Github.

This exposes a REST + JSON API that the other components communicate with. In order to publish anything on the update server, you will need a publisher account and at least one project. You will need [a publisher token](https://github.com/soatok/valence-updateserver#creating-publisher-tokens) to use the dev tools.

## Valence Dev Tools

The [dev tools documentation](https://github.com/soatok/valence-devtools#valence-dev-tools) fits well within the README on Github.

The devtools were designed so that you can quickly run the `ship` command to build, sign, and upload a new release all in one fell swoop, or break each step into an atomic command (i.e. to facilitate offline signatures with an airgapped machine).

## Libvalence

This is the meat and potatoes of this post: Making your code self-update.

My goal with this project was to ensure you don't need a cryptography engineering background to set this up properly. Once you have access to an update server and the dev tools installed, the rest of the work should just be using a simple API to solve this problem.

The API looks like this:

```
const { Bond, Utility } = require('libvalence');

let bond = Bond.fromConfig(
  'Project Name',
  __dirname + "/app", // Path
  ['https://valence.example.com'],
  [] // Serialized public keys (generated by dhole-crypto)
);

/**
 * @param {string} channel
 * @param {string|null} accessToken
 */
async function autoUpdate(channel = 'public', accessToken = null) {
  if (accessToken) {
    bond.setAccessToken(accessToken);
  }
  let obj = await bond.getUpdateList(channel);
  if (obj.updates.length < 1) {
    // No updates available
    return;
  }
  let mirror = obj.mirror;
  let update = obj.updates.shift();
  let updateInfo = await fetch.fetchUpdate(update.url, mirror, bond.verifier);
  if (updateInfo.verified) {
    await bond.applier.unzipRelease(updateInfo);
  }
}
```

You can also [ensure all updates are published on a cryptographic ledger](https://github.com/soatok/libvalence#usage), specify your own automatic update policy (the default policy is semver: patch updates are auto-installed, minor/major updates are not).

An important (but easily overlooked) feature is the concept of **release channels**.

You can, from the update server, generate access tokens that have access to a specific subset of channels (e.g. `public` and `beta` releases but not `alpha` or `nightly` releases).

This concept is implemented so that developers can offer exclusive access to early releases to their paid supporters (e.g. via Patreon), and bake that access directly into their automatic updates.

# Want to Contribute?

![Soatok appreciates you](https://soatok.com/static/stickers/Soatok_STICKERPACK-HEART.png)

All of these projects are open source on Github, but my development efforts are funded [through Patreon supporters](https://www.patreon.com/soatok).

I also stream most of my open source development [on my Twitch channel](https://www.twitch.tv/soatok).