The state of package signing across package managers
Tags: javascript, python, opensource, java
Published: 2020-06-11 14:49:58

---
title: The state of package signing across package managers
published: true
description: Recently we looked at the state of 2FA support across package managers. 2FA adds a layer of security by requiring two sources of authentication from maintainers when publishing packages.
tags: javascript, python, opensource, java
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/y4wouaalz5jxt88xmhuz.jpg
---

<p>Recently I looked at <a href="https://blog.tidelift.com/the-current-state-of-two-factor-authentication-across-package-managers" rel="noopener" target="_blank">the state of 2FA support across package managers</a>. 2FA adds a layer of security by requiring two sources of authentication from maintainers when publishing packages. This helps open source communities avoid supply-chain attacks by protecting packages from their author to their repository.<i>&nbsp;</i></p>
<!--more-->
<p><i><img src="https://f.hubspotusercontent30.net/hubfs/4008838/pkg-signing-pic-1.png" alt="pkg-signing-pic-1" width="381" style="width: 381px; display: block; margin: 0px auto;"></i></p>
<p>2FA is great, but hinges upon the package repository being secure, and isn’t an end-to-end verification that a package came from its maintainer.&nbsp;</p>
<p>But there’s another way that isn’t as dependent upon the package repository: cryptographic signing of packages. Let’s take a look at which platforms support package signing.</p>
<h2>But first, what is package signing?</h2>
<p>Package signing is <i>the act of an open source package (repo, binary, recipe, etc.) being cryptographically signed with a private key so that downstream users can verify the package with a public key</i>.</p>
<p>Across language ecosystems, there are generally two types of package signing:</p>
<ol>
<li><strong>Signed-by-repository</strong>: the repository signs uploaded packages, and users verify them after downloading.&nbsp;<br><img src="https://f.hubspotusercontent30.net/hubfs/4008838/package-signing-ic-2.png" alt="package-signing-ic-2" width="381" style="width: 381px; display: block; margin: 0px auto;"></li>
<li><strong>Signed-by-author</strong>: the author signs packages before uploading them to a repository, and users verify them after downloading from the repository. This is an end-to-end guard to ensure the package was uploaded by its maintainers.<br><img src="https://f.hubspotusercontent30.net/hubfs/4008838/pck-signing-pic-3.png" alt="pck-signing-pic-3" width="381" style="width: 381px; display: block; margin: 0px auto;"></li>
</ol>
<h2>Why is package signing useful?</h2>
<p>Whether you’re setting up a new codebase on your developer machine or deploying a webapp to your servers, you’re probably downloading dozens—or hundreds—of open source packages. It’s impractical to comb through every line of code to make sure the package you received was not tampered with. Package signing offers a way to say I trust this maintainer and I am guaranteed that this code was uploaded by them.</p>
<h2>Which programming language package managers support package signing?</h2>
<p>Let’s try to classify what each package manager does currently:</p>
<h3>Author-signing:</h3>
<ul>
<li><a href="https://docs.microsoft.com/en-us/nuget/create-packages/sign-a-package" rel="noopener" target="_blank"><strong>Nuget</strong></a><strong>: </strong>as of nuget cli 4.6.0 (March 2018), packages can be <a href="https://docs.microsoft.com/en-us/nuget/create-packages/sign-a-package" rel="noopener" target="_blank">signed</a> with certificates from a list of <a href="https://docs.microsoft.com/en-us/security/trusted-root/participants-list" rel="noopener" target="_blank">trusted Certificate Authorities</a>, and <a href="https://docs.microsoft.com/en-us/nuget/reference/cli-reference/cli-ref-verify" rel="noopener" target="_blank">verified</a> against those CAs or a specific set of key fingerprints. Nuget also supports <a href="https://docs.microsoft.com/en-us/nuget/api/repository-signatures-resource" rel="noopener" target="_blank">repository-signing</a>.</li>
<li><a href="http://maven.apache.org/plugins/maven-jarsigner-plugin/index.html" rel="noopener" target="_blank"><strong>Maven</strong></a><strong>/</strong><a href="https://docs.gradle.org/current/userguide/signing_plugin.html#signing_plugin" rel="noopener" target="_blank"><strong>Gradle</strong></a><strong>/</strong><a href="https://ant.apache.org/manual/Tasks/signjar.html" rel="noopener" target="_blank"><strong>Ant</strong></a><strong>: </strong>all packages uploaded to Maven Central <a href="https://central.sonatype.org/pages/requirements.html#sign-files-with-gpgpgp" rel="noopener" target="_blank">are required to be PGP-signed</a>, and all three package managers have tooling to sign and verify.</li>
<li><a href="https://docs.ruby-lang.org/en/2.7.0/Gem/Security.html" rel="noopener" target="_blank"><strong>Rubygems</strong></a>: authors can sign packages using SSL certificates based on RSA keys. Verification offers <a href="https://docs.ruby-lang.org/en/2.7.0/Gem/Security.html#module-Gem::Security-label-Signed+gems+and+security+policies" rel="noopener" target="_blank">several levels</a> of signature checks.&nbsp;</li>
<ul>
<li><i>Caveat</i>: supports Certificate Authorities but <a href="https://docs.ruby-lang.org/en/2.7.0/Gem/Security.html#module-Gem::Security-label-Certificate+chains" rel="noopener" target="_blank">doesn’t make any CA recommendations for the ecosystem</a>, so there’s no central CA.&nbsp;</li>
<li><i>Usage: </i>we found that, as of March 2020, only 1.4% (2,216 of 157,640 gems) of latest-version gems on Rubygems.org were signed.</li>
</ul>
</ul>
<h3>Repository-signing:</h3>
<ul>
<li><a href="https://docs.npmjs.com/about-pgp-signatures-for-packages-in-the-public-registry" style="background-color: transparent;" rel="noopener" target="_blank"><strong>npm</strong></a><span style="background-color: transparent;">: npm signs packages with its own PGP key, which is publicized on </span><a href="https://keybase.io/npmregistry" style="background-color: transparent;" rel="noopener" target="_blank">Keybase</a><span style="background-color: transparent;">.</span>
<ul>
<li><i>Caveat</i>: although npm doesn’t have native author-signing, there is some 3rd-party tooling available via the <a href="https://medium.com/redpoint/introducing-pkgsign-package-signing-and-verification-for-npm-5b833e0ec2d4" rel="noopener" target="_blank">pkgsign</a> library<strong>.</strong></li>
<li><i>More: </i>some interesting discussions about signing have happened on <a href="https://github.com/node-forward/discussions/issues/29" rel="noopener" target="_blank">node-forward</a>, and <a href="https://github.com/npm/npm/pull/4016" rel="noopener" target="_blank">npm</a>.</li>
</ul>
</li>
</ul>
<h3>In progress:</h3>
<ul>
<li><a href="https://docs.python.org/3/whatsnew/2.5.html?highlight=gpg#pep-314-metadata-for-python-software-packages-v1-1" rel="noopener" target="_blank"><strong>Pypi</strong></a>: Python 2.5 added support for author-signing with GPG (via <span style="font-family: 'courier new', courier;">python setup.py --sign upload</span>), but there is no built-in support to verify those signatures yet.</li>
<ul>
<li><i>Caveat</i>: there are <a href="https://discuss.python.org/t/which-cryptographic-signing-approach/2241" rel="noopener" target="_blank">active discussions</a> and PEPs around signing packages—<a href="https://www.python.org/dev/peps/pep-0458/" rel="noopener" target="_blank">PEP 458</a> for repository-signing and <a href="https://www.python.org/dev/peps/pep-0480/" rel="noopener" target="_blank">PEP 480</a> for author-signing—using <a href="https://theupdateframework.io/" rel="noopener" target="_blank">The Update Framework</a>.</li>
</ul>
<li><a href="https://make.wordpress.org/core/2019/05/17/security-in-5-2/" rel="noopener" target="_blank"><strong>Wordpress</strong></a><strong>: </strong>discussion of signing happened <a href="https://core.trac.wordpress.org/ticket/25052" rel="noopener" target="_blank">here</a>, and signing of Wordpress itself was added <a href="https://core.trac.wordpress.org/ticket/39309" rel="noopener" target="_blank">here</a>. As of 5.2, WordPress updates are now signed, but plugins and themes are still unsigned.</li>
<ul>
<li><i>Caveat: </i>there’s an open <a href="https://core.trac.wordpress.org/ticket/49200" rel="noopener" target="_blank">discussion</a> about implementing author-signing via a PKI called <a href="https://github.com/paragonie/libgossamer/tree/master/docs" rel="noopener" target="_blank">Gossamer</a>.</li>
</ul>
</ul>
<h3>Partial or no signing:</h3>
<ul>
<li><a href="https://blog.golang.org/module-mirror-launch#TOC_3." style="background-color: transparent;" rel="noopener" target="_blank"><strong>Go Modules (Go)</strong></a><strong style="background-color: transparent;">: </strong><span style="background-color: transparent;">as of Go 1.13, Go Modules verifies downloaded packages (which are usually git repos) against a </span><a href="https://sum.golang.org/" style="background-color: transparent;" rel="noopener" target="_blank">checksum database</a><strong style="background-color: transparent;">.</strong></li>
<li><a href="https://getcomposer.org/doc/02-libraries.md" rel="noopener" target="_blank"><strong>Composer</strong></a><strong> (PHP): </strong>there have been discussions of built in signing/verification <a href="https://github.com/composer/composer/issues/4022" rel="noopener" target="_blank">here</a>.</li>
<li><strong>Cargo (Rust): </strong>crates are hosted on GitHub*, and the Rust community has discussed package signing <a href="https://internals.rust-lang.org/t/pre-pre-rfc-solving-crate-trust/6495" rel="noopener" target="_blank">here</a>, <a href="https://github.com/rust-lang/rfcs/pull/2474" rel="noopener" target="_blank">here</a> and <a href="https://github.com/rust-lang/crates.io/issues/75" rel="noopener" target="_blank">here</a>.</li>
<li><strong>CPAN (Perl): </strong>no built-in support, but author-signing is available via 3rd party package manager <a href="https://metacpan.org/pod/pp" rel="noopener" target="_blank">pp</a> (or the underlying <a href="https://metacpan.org/pod/Module::Signature" rel="noopener" target="_blank">cpansign</a> cli)</li>
<li><strong>Carthage (Cocoa): </strong>packages are hosted on GitHub*, GitLab**, Bitbucket***, etc</li>
<li><strong>Julia Pkg (Julia): </strong>packages are hosted on GitHub*, GitLab**, Bitbucket***, and registered with the <a href="https://github.com/JuliaRegistries/General" rel="noopener" target="_blank">Julia Package Registry</a> on github.</li>
<li><strong>Bower (JS): </strong>packages are hosted on GitHub*, GitLab**, Bitbucket***, etc</li>
<li><a href="https://puppet.com/docs/puppet/latest/modules_publishing.html" rel="noopener" target="_blank"><strong>PDK</strong></a><strong> (Puppet)</strong></li>
<li><a href="https://atmospherejs.com/i/publishing" rel="noopener" target="_blank"><strong>Meteor</strong></a><strong> (Meteor)</strong></li>
<li><a href="https://cabal.readthedocs.io/en/latest/developing-packages.html" rel="noopener" target="_blank"><strong>Cabal</strong></a><strong> (Haskell)</strong></li>
<li><a href="https://hex.pm/docs/publish" rel="noopener" target="_blank"><strong>Mix</strong></a><strong> (Erlang)</strong></li>
<li><a href="https://cran.r-project.org/doc/manuals/r-release/R-exts.html" rel="noopener" target="_blank"><strong>R</strong></a><strong> (R)</strong></li>
</ul>
<h2>Does package signing matter?</h2>
<p>If you read through the discussions linked to above, package signing is indeed a lofty goal and challenging to get right. So is it worth it, and could it prevent any classes of exploits? You can find some examples of supply chain attacks from the past decade documented <a href="https://github.com/theupdateframework/pip/wiki/Attacks-on-software-repositories" rel="noopener" target="_blank">here</a> and <a href="https://github.com/in-toto/supply-chain-compromises" rel="noopener" target="_blank">here</a>. Those lists contain some examples of repository account takeovers and other exploits that could be mitigated using package signing.&nbsp;</p>
<h2>More reading</h2>
<p>Here are some more writeups on package signing that are useful or that we found interesting:</p>
<ul>
<li><a href="https://theupdateframework.io/" rel="noopener" target="_blank">The Update Framework, for “securing software update systems”</a></li>
<li><a href="https://devblogs.microsoft.com/nuget/NuGet-Package-Signing/" rel="noopener" target="_blank">How NuGet package signing was planned</a> and <a href="https://haacked.com/archive/2019/04/03/nuget-package-signing/" rel="noopener" target="_blank">Why NuGet Package Signing Is Not (Yet) for Me</a> (Nuget)</li>
<li><a href="https://www.davidfischer.name/2012/05/signing-and-verifying-python-packages-with-pgp/" rel="noopener" target="_blank">Signing and Verifying Packages with PGP</a> (Pypi)</li>
<li><a href="https://www.grant-olson.net/news/2013/09/29/nobody-cares-about-signed-gems.html" rel="noopener" target="_blank">Nobody cares about signed gems</a> (Rubygems)</li>
</ul>
<h3>Footnotes:</h3>
<p>* GitHub allows <a href="https://help.github.com/en/github/authenticating-to-github/managing-commit-signature-verification" rel="noopener" target="_blank">commits and tag signing</a> and will label them as verified-or-not, although there are no signatures/checksums available for release zip/tar archives.&nbsp;</p>
<p>** GitLab allows <a href="https://docs.gitlab.com/ee/user/project/repository/gpg_signed_commits/" rel="noopener" target="_blank">commit signing</a> and labels them as verified or not, but not for builds.</p>
<p>*** BitBucket Server allows <a href="https://confluence.atlassian.com/bitbucketserver/using-gpg-keys-913477014.html" rel="noopener" target="_blank">commit and tag signing</a> -- while Bitbucket Cloud <a href="https://jira.atlassian.com/browse/BCLOUD-3166?_ga=2.248503567.94323029.1582068664-710106391.1582068664" rel="noopener" target="_blank">does not yet</a> -- but not for builds.</p>
<p>&nbsp;</p>