Opened 6 years ago
Last modified 5 years ago
#49200 new enhancement
Allow Developers to Cryptographically Sign Their Own Plugins/Themes with the Gossamer Public Key Infrastructure (PKI)
| Reported by: |
|
Owned by: | |
|---|---|---|---|
| Milestone: | Awaiting Review | Priority: | normal |
| Severity: | normal | Version: | 5.4 |
| Component: | Upgrade/Install | Keywords: | |
| Focuses: | Cc: |
Description
In #39309, we attempted to address the issue of cryptographically verifying WordPress core updates.
Cryptographically signing your software prevents adversaries with control over your network and/or the backend infrastructure (api.wordpress.org) from installing malware on your machine. This is especially crucial with automatic updates. WordPress has had βat least one near-miss caused by a lack of cryptographic signatures in its auto-updater over the years.
The Internet may not survive if β35.5% of websites on the Internet were conscripted into a DDoS botnet. The damage is difficult to calculate, but the numbers are sufficiently big that you can guarantee your sysadmins will be very cross with whoever pulls off such a blatantly destructive criminal act. It's in the best interest of everyone (even people who dislike WordPress, or more broadly, dislike PHP software) that we solve this problem before a worst case scenario can happen.
It's also important that the solution have the following properties:
- Security. The solution should use the best cryptography primitives available, and combine them in obviously secure ways.
- Simplicity. The solution should be as simple as possible, to minimize attack surface and risk of bugs.
- Auditability. It should be impossible for even nation state adversaries to introduce targeted stealth backdoors in WordPress core or any theme/plugin.
- Freedom. It should stand alongside the principles of Free Software by respecting user freedom and developer freedom.
Building atop existing code signing solutions fails point 2 of the above list, especially if X.509 is involved. Neither Mozilla nor Microsoft can reliably implement X.509 securely; what chance does anyone else have? (Arguably, it would fail 1, 2, and 4; without Binary Transparency, it fails all four.)
Eschewing a PKI in favor of just having a centralized repository sign everything fails property fails point 4. Developers should be in control of their own signing keys. It's their code, they should be the ones to authorize new releases; not some centralized middleman.
Paragon Initiative Enterprises has designed a solution that offers all four of the properties we outlined above. We call our solution Gossamer (named after the transparency and interconnected nature of the solution), which we have discussed in the other ticket.
TL;DR for Non-Experts
At the end of this work, WordPress will have a new API that looks like this (actual function name is negotiable):
<?php /** @var array<array-key, string> $vKeys */ $vKeys = wp_fetch_verification_keys( 'wordfence' );
Caveats
This will fetch the verification keys for a given provider (whoever develops your favorite plugins and themes), which can then be used to verify the signature attached to a new release.
The rest of the complexity will be mostly abstracted away by this function. You may have to make a choice in your wp-config.php file, however:
- Manage verification keys locally.
- Pro: Better security, ideal for paranoid setups.
- Con: Uses MySQL disk space.
- Outsource your verification to a trusted third party (e.g. your hosting provider)
- Pro: No extra disk space needed.
- Con: You have to trust your hosting provider. (Which is probably a given.)
We don't have any strong opinions on which of these two should be the default. Many low-end WP systems will almost certainly be better served by option 2, but option 1 is more secure by default.
Option 2 requires some way of specifying which third party to trust (or a simple way for your hosting providers to easily and securely inject this configuration in all of their customers' environments by default).
What's Going On Under the Hood?
The Gossamer Public Key Infrastructure (Gossamer PKI) allows end users to securely associate Public Keys with an Identity, without the use of trusted Certificate Authorities or complicated decentralized models (e.g. Web of Trust).
(There is one optional feature that acts like an Authority; called the βSuper Provider. We recommend this for WordPress, for practical reasons, but the protocol still works securely without one.)
Above, we used the term verification key. This is the same thing as an Ed25519 public key. However, the lingo can get confusing, so here's quick decoder ring.
| Cryptography Terminology | Gossamer Terminology |
|---|---|
| Ed25519 Secret Key | Signing Key |
| Ed25519 Public Key | Verification Key |
Gossamer achieves this by using a type of Verifiable Data Structure called a cryptographic ledger, which is published from a central hub and then replicated in a decentralized manner.
The cryptographic ledger we use is called βChronicle. Chronicle is append-only; history is immutable and deterministic.
- Immutable: History cannot be changed.
- Deterministic: If you start a Gossamer instance with an empty database and replay a Chronicle, you will always end up at the same state as an actively-updated Chronicle.
Although the underlying data structure is deterministic, Gossamer supports revocation by applying a βhigher-level protocol with a distinct grammar and simple messaging life-cycle.
Every action (appending/revoking updates, appending/revoking verification keys) adds a new message to the ledger, but may result in a list of strings growing or depleting.
Further Reading
- βlibgossamer documentation
- βGossamer API Server
- If you outsource your trust to a web host, this standalone REST API is what your WordPress blog will be talking to
The Purpose of This Ticket
WordPress needs the secure update problem solved, and Gossamer is the best fit for WordPress's needs. In order to implement Gossamer, these are the following steps that need to be taken (not including the work already done).
- A Chronicle Instance owned by the WordPress community needs to be spun up.
- An Infrastructure Code Change is needed, that does all of the following:
- Allow developers to publish/revoke their verification keys (either through the wordpress.org website or a REST API).
- Publishes new records onto the Chronicle instance owned by the WordPress community.
- A WordPress Core Patch is needed, built atop libgossamer, that either federates trust to a third party, or handles it locally.
- For federated trust, we need to be all on the same page about configuration changes. Users will need to be able to specify a URL and public key for their hosting provider's infrastructure, in order for trust to federate.
- For local trust, libgossamer's
Synchronizerclass and a reasonable amount of available disk space for MySQL is all you need. - This will contain the
wp_get_verification_keys()API, at a minimum.
- Replicas. Many Chronicle instances from various community leaders should be spun up, to actively replicate the main Chronicle instance outlined in step 1.
Once we've crossed the threshold of step 4, we will be able to reliably fetch the currently-trusted verification keys for any arbitrary WordPress plugin/theme developer in the world from a simple function call.
To verify that a plugin/theme download is authentic, the API should look something like this:
<?php if (wp_update_is_valid( $filename, $provider, $package, $version )) { // Actually install it. }
This is a wrapper for a more pedantic implementation:
<?php /** * @param string $filename e.g. /path/to/extra.zip * @param string $provider e.g. wordfence * @param string $package e.g. premium * @param string $version e.g. 4.18.3 * @return bool */ function wp_update_is_valid( $filename, $provider, $package, $version ) { /** @var array<array-key, string> $vKeys */ $vKeys = wp_fetch_verification_keys( 'wordfence' ); $update = wp_fetch_version_info( $provider, $package, $version ); if (empty($update['signature'])) { return false; } $signature = $update['signature']; foreach ($vKeys as $vKey) { if (wp_verify_signature( $filename, $signature, $vKey )) { return true; } } $vKeys = wp_fetch_verification_keys( WORDPRESS_SUPER_PROVIDER ); foreach ($vKeys as $vKey) { if (wp_verify_signature( $filename, $signature, $vKey )) { return true; } } return false; }
In order for wp_update_is_valid() to return TRUE, the following must be true:
- The update info is published in the Chronicle ledger.
- The update was never revoked with a subsequent Chronicle record.
- The signature included with the Chronicle record for this particular update is valid for the file you downloaded.
- The signature is valid for one of the currently-trusted verification keys for the provider (or the Super Provider).
What might not be obvious: As long as someone designs a mechanism to verify that βall updates are reproducible from their source code, this completely solves the βTriangle of Secure Code Delivery.
Once implemented, Gossamer will give WordPress the most secure open source software supply chain in the world.
(To the security industry snobs that look down on PHP/WordPress developers: Put that in your perspective pipe and smoke it!)
Timeline Roadmap
N/A, yet.
This is what we need the most input on from the WordPress community.
We'd like to have a prototype ready for 5.5, and to be enforcing update signatures as early as 5.6.
This may not be realistic. ASAP is the best answer we can give right now.
Immediate Questions
Regardless of your expertise level, we'd like to know the community's thoughts on the following:
- Default to local or federated?
- Where/how should we store Gossamer configuration? (Especially with the ease of WordPress hosting providers for federated key management in mind.)
- Timeline feedback?