[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