Signing WordPress Packages and Updates

With package signatures you can ensure that the code you’re running is signed by the vendors you trust. The concept involves two fundamental items:

  1. a secret signing key that is known only to the vendor, and
  2. it’s matching public key that you mark as trusted.

Using the underlying math of the public-key cryptography, you’re able confirm that the ZIP files downloaded by your WordPress instance were signed by the vendors you trust. The same core principle is used by software updates to your phone, car and potentially fridge.

WordPress package signing
Flowchart of establishing trust by storing the vendor’s public key, and using it to verify the signatures of all packages.

The Hardest Thing

Is to show trust without relying on a single authority.

With app stores on phones everything is controlled by a single company who implant a specific public key in your device which then makes it trust all other keys derived from it (used by individual app developers).

Most Linux distributions rely on a similar hard-coded list of trusted public keys. All software available from their official package repository is signed by the matching private keys. That’s why using a package from outside the official repository (such as Node.js) requires importing the authors custom public key.

With the distributed future of the WordPress core, plugin and theme distribution, the challenge is to build a user interface for managing the trusted keys that is easy to use and feels reliable.

Support in WordPress Core

The first version of package signature verification was added in WordPress 5.2 after long discussions in the feature proposal ticket. It is implemented as part of the download_url() function and intended only for verifying WP core updates:

  1. When downloading the package ZIP, it checks if the hostname of the URL belongs to a list of hostnames wp_signature_hosts with signature verification enabled.
  2. If yes, then it attempts to extract the package signature from the HTTP response header X-Content-Signature or download it from the same URL but with the .sig suffix.
  3. If the signature is found, it is then checked against the list of known public keys wp_trusted_keys.
  4. If the signature is missing or the verification fails, it returns a “soft-fail” error but proceeds to use the downloaded ZIP unless the wp_signature_softfail filter is set to return false.

Cryptographic Components

The actual cryptography is based on the following two standards:

  1. SHA384 file hashes, and
  2. Ed25519 private-public key pairs.

Having the decision to use these two primitives allows us to focus on the implementation and usability of the solution instead of arguing over cryptographic alternatives and their potential benefits.

Proposal for Distributed Package Signing

Below is a detailed proposal for how this could be implemented across multiple distributed package vendors:

Distributed WordPress package signing and verification process
Distributed WordPress package signing and verification process.

Risks and Concerns

The following scenarios have to be considered when designing and building this system:

  1. The vendor signing keys are compromised (made public) which allows anyone to create a package signed by a trusted key.
    • The original vendor should be able to mark the key as invalid (revoked).
    • The WP instances should stop trusting and using packages signed with revoked keys.
  2. The vendor wants to change their private key.
    • The vendor should be able to publish and sign the new key. Old keys should be marked as invalid (revoked).
  3. Vendor looses access to the UpdateURI defined in their packages (expired domain). All update checks fail.
    • Users should be notified of the failed update checks in the WP admin or over email.
  4. Vendor packages must be kept isolated in their own namespace (directory prefix or suffix) to ensure another trusted vendor can’t publish and override the same package.

All of these issues are solved by a system where users are asked to curate the trusted keys and only a single key per vendor is supported at all times. This is the implementation in the Update Pilot client plugin.


Discussion

Leave a Reply

Your email address will not be published. Required fields are marked *