WordPress.org

Make WordPress Core

Opened 6 days ago

Last modified 5 days ago

#49200 new enhancement

Allow Developers to Cryptographically Sign Their Own Plugins/Themes with the Gossamer Public Key Infrastructure (PKI)

Reported by: paragoninitiativeenterprises Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: trunk
Component: Upgrade/Install Keywords:
Focuses: Cc:
PR Number:

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:

  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 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:

  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 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

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
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:

  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 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:

  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?

Change History (4)

This ticket was mentioned in Slack in #core by paragonie. View the logs.


6 days ago

#2 @ayeshrajans
6 days ago

Thank you for posting the ticket @paragoninitiativeenterprises . I'm not a decision maker here, but I would like to put my thoughts up as someone who is active/invested in Drupal and Phar-io package updates and signing.

Without going in detail for possible padding attacks or possibility to select to bad ciphers, I find the update signing in Composer and Phar-IO to be least invasive yet functional. They rely on openssl and gpg, which had several security vulnerabilities, but are trusted available to be used by pretty much every LAMP stack.

I appreciate the amazing and often unfathomable effort you have put in developing these libraries such as Sodium compat, Chronicle, and now Gossamer. I'm sure those who use it will certainly appreciate it too. If this were to go in WordPress core, I would like to point out the amount of trust WordPress community has to put on your work. I remember the last time this topic came up, the conversation was stalled at a point about lack of an audit in paragonie/sodium_compat package. A fully implemented Gossamer + Chronicle instances would assume many packages from the same vendor:

  • paragonie/certainty
  • paragonie/easydb
  • paragonie/sodium_compat
  • paragonie/slim-sapient
  • paragonie/blakechain
  • paragonie/sapient

Further, because Chronicle package builds on top of Slim, we will be trusting slim/slim and related packages too.

I doubt there are other active members in WordPress community that are familiar with these libraries (other than you of course). In addition to trusting all your packages, this will add maintenance overhead for package updates, security releases, and general bug fixes. If it takes 2 versions to add a package, it would take at least 5 to remove it. Libraries that once had maintainers are not practically maintained by WordPress community, such as kses and a considerable amount of packages in wp-includes.

WordPress already carries several libraries in both PHP and JS departments, and keeping up with their updates is not easy, nor happens in a regular or pragmatic fashion. Because of the lack of an audit, and the novel approach instead of using established PKI, all these 35.5% of web sites would be indirectly trusting your organization to keep the word and take your PKI approach instead.

I think the question ultimately comes down to the politics and trust considerations whether WordPress community is ready to trust all the packages above to function correctly, will be maintained, secure, and whether the PKI approach can stand against the masses. OpenSSL package probably had been reviewed many times by bounty hunters and security professionals, and yet Heartbleed took a long time to be discovered. What sort of trust do we (as WordPress community) has for Gossamer to be included by default in world's most wide-spread web platform?

Although WordPress comes as a single easy to use package, most modern deployments in fact use packages projects and packages such as wpackagist, johnpbloch/wordpress and roots/bedrock. I also maintain WordPress-Security-Advisories. None of these packages come bundled, and they have to earn the trust of the community. As someone who wishes WordPress (and many PHP packages for that matter) to adopt automatic updates that trust for developers are established without secure channels or middleman, I really wish your efforts will fruitful. May I ask what prevents you from providing this solution as a plugin? I'm sure WordPress provides necessary hooks to validate updates before they are written to disk. If the necessary hooks do not exist, it would be relatively easy and bite-size patches to WordPress core to be accepted.

If the community accepts this plugin as a de-facto update guard plugin, I'm sure the decision makers will be inclined to ship the solution along with WordPress core.

#3 @paragoninitiativeenterprises
5 days ago

Hi @ayeshrajans. Thank you for the insightful comment.

I think you misunderstood the architecture a bit.

The only package that will be installed on any WordPress machine is libgossamer, not gossamer-server (which can also exist as a WordPress plugin so people don't have to trust external servers that run slim, etc.). The version of libgossamer that will be included in the WP core patch will only depend on wpdb, Wp_Http, and the already-present sodium_compat. (We aren't using Composer to automatically fetch dependencies when we write a WordPress patch.)

A lot of the concerns about external dependencies and trust follow from this misunderstanding, so I shall not dwell on them further. If you feel any of them are still worthy of examination in the context of a libgossamer dependency, not a gossamer-server dependency, please let me know which ones. We don't want to beat a dead horse (as the idiom goes), but we also want to make sure any standing criticisms are fully discussed.

I find the update signing in Composer and Phar-IO to be least invasive yet functional.

Incidentally, we are preparing a congruent proposal for Composer/Packagist.

The problem with signed Phars is that you can strip them easily. Simply delete the pubkey file and replace the OpenSSL signature with a SHA1 hash and PHP will happily run unsigned code again.

They rely on openssl and gpg, which had several security vulnerabilities, but are trusted available to be used by pretty much every LAMP stack.

OpenSSL and PGP would downgrade our proposal to use:

The experts in software security and cryptography are almost unanimously opposed to the incumbent tools. The reasons are varied, but mostly:

  1. They introduce a lot of unnecessary complexity, which makes the security predictions pessimistic.
  2. They invite you to chase the lowest denominator of cryptography primitives (e.g. RSA with PKCS1v1.5 padding) rather than simply secure boring crypto (PDF).
  3. Many of these projects were designed by hobbyists and advocated by activists who were well-meaning but largely out of their depth when it comes to secure protocol design.

Aside: Libgcrypt (which powers GnuPG) is basically an entire undergraduate course in side-channel cryptanalysis. Instead of fixing GPG, experts are designing tools to replace it entirely, like age and minisign. Do not resuscitate.

With all that in mind, opting for RSA/PGP/X.509 isn't being conservative with security. It's regressive, in this case.

Because of the lack of an audit

If an audit would give the WordPress community confidence in what's being offered, surely the WordPress community can fund one?

If anyone reading this is wondering why we insist the community fund an audit, ask yourself, "Is it reasonable to expect the person or team who's put hundreds of hours of unpaid cryptography engineering work into solving this problem, with no plans to monetize their solution, to further pony up tens or even hundreds of thousands of dollars out of their own pocket to pay their competitors to conduct an audit?"

If an audit is necessary, then it should be on the community to fund one.

If the community cannot or will not fund an audit, then the "lack of an audit" objection has no standing and should be put to rest in future discussions about this project unless the funding calculus changes.

and the novel approach instead of using established PKI, all these 35.5% of web sites would be indirectly trusting your organization to keep the word and take your PKI approach instead.

What trust does the WordPress community have to give us, exactly?

  • The entire Gossamer PKI is deterministic and auditable. No one can abuse it without getting caught.
  • We will not have any privileged position (e.g. the "Super Provider"; the WordPress security team will have control over that).
  • Our designs are 100% open source, and permissively licensed.

The standard for cryptographic transparency is called "nothing up my sleeves", wherein all design decisions are justified clearly and nothing gives the designer an unfair advantage (e.g. a backdoor).

Gossamer isn't just "nothing up my sleeves" transparency, it's more like "stark naked with an RFID tracking chip that anyone can monitor" levels of transparency.

The only trust that anyone has to have is that we implemented the algorithms correctly (but only if you don't have ext/sodium installed) and that our protocol is actually secure.

(The entire point of Gossamer's design is to keep power in the hands of the community, rather than an elite class of experts. We abhor power-seeking behavior.)

I think the question ultimately comes down to the politics and trust considerations ... and whether the PKI approach can stand against the masses.

(Snipped for the above consideration about dead horses.)

If the answer turns out to be "No, the PKI approach cannot stand against the masses," then it will require an scientific breakthrough in modern cryptanalysis and secure protocol design.

Breakthroughs are difficult to predict, but given the slowed rate of hash functions being broken (SHA-2 is still secure, despite decades of attempts), it seems unlikely to happen.

What sort of trust do we (as WordPress community) has for Gossamer to be included by default in world's most wide-spread web platform?

This is an excellent question that everyone should ask themselves earnestly. We'd love to hear what the community needs, and then outline a strategy to meet those needs.

If the WordPress community feels that they need cryptography experts to chime in, Paragon Initiative Enterprises is kind of the only game in town for PHP cryptography. (This wasn't our doing, either. We abhor gate-keeping.)

As someone who wishes WordPress (and many PHP packages for that matter) to adopt automatic updates that trust for developers are established without secure channels or middleman, I really wish your efforts will fruitful.

Thanks!

May I ask what prevents you from providing this solution as a plugin?

Insecure by default means insecure in practice. Making this an optional feature means most people will not benefit from better security.

I mean, for all any of us know, this could all be for naught. While we're discussing ways to prevent a supply-chain attack, someone else could be ready to press the Enter key on their exploit to burn it all down. There are no guarantees in life, we just have to do the best we can.

A temporary plugin can absolutely be developed for testing purposes prior to mass adoption, but the end goal should absolutely be ubiquitous deployment to the entire ecosystem.

This ticket was mentioned in Slack in #core by paragonie. View the logs.


5 days ago

Note: See TracTickets for help on using tickets.