[wp-trac] [WordPress Trac] #49200: Allow Developers to Cryptographically Sign Their Own Plugins/Themes with the Gossamer Public Key Infrastructure (PKI)
WordPress Trac
noreply at wordpress.org
Wed Jan 15 08:12:21 UTC 2020
#49200: Allow Developers to Cryptographically Sign Their Own Plugins/Themes with
the Gossamer Public Key Infrastructure (PKI)
------------------------------------------+-----------------------------
Reporter: paragoninitiativeenterprises | Owner: (none)
Type: enhancement | Status: new
Priority: normal | Milestone: Awaiting Review
Component: Upgrade/Install | Version: trunk
Severity: normal | Keywords:
Focuses: |
------------------------------------------+-----------------------------
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
[[https://www.wordfence.com/blog/2016/11/hacking-27-web-via-wordpress-
auto-update/|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
[[https://w3techs.com/technologies/overview/content_management|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:
1. '''Security.''' The solution should use the best cryptography
primitives available, and combine them in obviously secure ways.
2. '''Simplicity.''' The solution should be as simple as possible, to
minimize attack surface and risk of bugs.
3. '''Auditability.''' It should be impossible for even nation state
adversaries to introduce targeted stealth backdoors in WordPress core or
any theme/plugin.
4. '''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
[[https://core.trac.wordpress.org/ticket/39309#comment:91|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
<?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:
1. Manage verification keys locally.
* Pro: Better security, ideal for paranoid setups.
* Con: Uses MySQL disk space.
2. 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
[[https://github.com/paragonie/libgossamer/blob/master/docs/specification/Protocol.md
#the-super-provider|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
[[https://github.com/paragonie/chronicle|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
[[https://github.com/paragonie/libgossamer/tree/master/docs#the-life-
cycle-of-a-gossamer-communication|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
* [[https://github.com/paragonie/libgossamer/tree/master/docs|libgossamer
documentation]]
*
[[https://github.com/paragonie/libgossamer/tree/master/docs/reference|API
reference]]
*
[[https://github.com/paragonie/libgossamer/tree/master/docs/specification|Specification]]
* [[https://github.com/paragonie/gossamer-server|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).
1. A '''Chronicle Instance''' owned by the WordPress community needs to be
spun up.
2. An '''Infrastructure Code Change''' is needed, that does all of the
following:
1. Allow developers to publish/revoke their verification keys (either
through the wordpress.org website or a REST API).
2. Publishes new records onto the Chronicle instance owned by the
WordPress community.
3. 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 `Synchronizer` class 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.
4. '''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
<?php
if (wp_update_is_valid( $filename, $provider, $package, $version )) {
// Actually install it.
}
}}}
This is a wrapper for a more pedantic implementation:
{{{#!php
<?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:
1. The update info is published in the Chronicle ledger.
2. The update was never revoked with a subsequent Chronicle record.
3. The signature included with the Chronicle record for this particular
update is valid for the file you downloaded.
4. 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 [[https://reproducible-builds.org|all updates are reproducible
from their source code]], this completely solves the [[https://defuse.ca
/triangle-of-secure-code-delivery.htm|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:
1. Default to local or federated?
2. Where/how should we store Gossamer configuration? (Especially with the
ease of WordPress hosting providers for federated key management in mind.)
3. Timeline feedback?
--
Ticket URL: <https://core.trac.wordpress.org/ticket/49200>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list