Sign coconut

coconut (Photo credit: @Doug88888)

I’ve been spending a chunk of free time recently working on a few PRs for Composer related to security so this is my usual “let’s watch Paddy think aloud in a completely unstructured manner” blog post. But seriously, with all the issues and PRs going around, this is my detailed look at solving a “simple” problem: establishing trust in Composer’s source code and its operation thereafter.

The Composer issue, as initially reported by Kevin McArthur, was fairly simple. Since no download connection by Composer was properly secured using SSL/TLS then an attacker could, with the assistance of a Man-In-The-Middle (MITM) attack, substitute the package you wanted to download with a modified version that communicated with the attacker’s server. They could, for example, plant a line of code which sends the contents of $_POST to the attacker’s server.

The obvious solution is to implement TLS support…

To mitigate this risk, I updated Composer in a PR in the following ways:

  1. Peer verification is enabled by default. Disabling it nets you a continual warning message.
  2. It follows all recommended TLS options being introduced for PHP 5.6 (thanks to @rdlowrey).
  3. Since peer verification requires root CA certificates, Composer will attempt to locate a local system certificate bundle (thanks to @EvanDotPro).
  4. If all else fails, Composer bundles root CA certificates which it will fall back to.
  5. Users can override the default detected certificate bundle by manually setting a –cafile option for most commands.
  6. The Installer has also been updated for 1-5.

Composer should now operate with SSL/TLS protections out of the box. There may be edge cases since support for Subject Alternative Names (SANs) in SSL certs has not yet been added to PHP 5.4 or 5.5, but I’m hoping that future releases of these versions will see it added. This particular issue does not impact Packagist.

Mission accomplished?

There are other TLS related features that can be looked into for the future. Users may want to generate their own CA cert bundle file as a substitute, e.g. Evan Coury’s Sslurp, to avoid a single point of failure or trust. With the dawning realisation that government surveillance is commonplace, and that trusted CA’s may mistakenly issue, or allow to be issued, certificates for entities without that entity’s permission, public certificate pinning may also warrant future attention.

SSL/TLS should protect the TCP connections between the client and the server, but it doesn’t actually verify that the code being downloaded was published by a trusted maintainer – only that you downloaded it from a verified host. So, what if the server were compromised? What if the SSL/TLS connection were breached?

Throughout 2013/2014, TLS has been besieged by a number of problems:

  1. Weaknesses in the protocol: SSL/TLS is made up of SSL 2.0, SSL 3.0, TLS 1.0, TLS 1.1 and TLS 1.2. Newer versions tend to be stronger, and SSL is overdue to be phased out of existence. Aside from obsolescence, the protocols are constantly under the microscope. On 4 March, researchers released details for the new “Triple Handshake Attack”. You’re probably already familiar with terms like CRIME and BEAST from 2013. Another part of the protocol is how encryption is implemented. TLS may use quite a number of cipher suites (named sets of authentication, encryption and MAC algos) in any particular order of preference. Reordering the preference to favour stronger ciphers which have Perfect Forward Secrecy (PFS) as an attribute is essential to mitigate against the loss of private keys (genuine loss, stolen, demanded by a court order or NSL). Without PFS, one could decrypt previously logged requests once they had the private key. PFS is not favoured by default in openssl or curl, but it is as part of the PHP 5.6 overhaul in PHP streams.
  2. Weaknesses in the Certificate Authority: CAs, like any entity, are capable of mistakes. In the past, some CAs have mistakenly released, or had stolen, trusted certificates which were used to create new certificates to impersonate entities like Google or to sign software, e.g. as Adobe. This is one reason why it is essential for any software bundling CA certificates to have a process for updating them regularly. There have been lots of CA problems. For example, in 2012, Trustwave publicised a worrying practice among CAs. Trustwave had been selling hardware implanted with subordinate certificates for the specific purposes of generating keys for domains that their customers did not necessarily own, and they insinuated that other CAs had similar businesses. One can imagine what the NSA would do with such a machine.
  3. Weakness in implementation: Apple screwed it up big time in OS X and iOS. GnuTLS was just patched for a similar issue and I was installing updates yesterday. PHP’s native SSL defaults are both insecure and buggy, to be fixed primarily in PHP 5.6. The NSA was reading unsecured intra-datacentre traffic from Google, Yahoo, Microsoft and probably most of the planet beyond those three. Companies like to monitor what employees send over HTTPS (if you work for one, check your company browser to see what certificates are installed). There also remains a culture in programming (it’s not solely PHP) of disabling peer verification rather than deal with error conditions. Interesting note: Apple has also just managed to disabled the mitigation for BEAST in Safari 7 for all platforms other than OS X 10.9 and iOS 7 – leaving Windows and earlier OS X versions potentially vulnerable.

Relying on TLS blindly has another name: being naïve. You could write a book on TLS problems.

So even with TLS implemented and working properly, users will still have trust issues. The number of times I’ve seen complaints about piping code from a download straight into the PHP interpreter (as is suggested when installing Composer) makes that abundantly clear. This approach violates the idea of knowing what you’re executing but it’s more relevantly a problem with establishing trust. We implicitly trust those who write Composer not to attack us – there’s just nothing specifically binding that trust to composer.phar and its installer.

As the recent GnuTLS vulnerability has highlighted, there is actually an app for this! In the Ubuntu/Debian ecosystem we download code. Even with TLS broken (as we know it definitely was until yesterday), we still have some faith in the packages we’ve been downloading for years because they employ an additional defence against substitution – they are signed.

Package Signing

File signing is a simple notion. A trusted author/maintainer signs a file with a private key that only they possess. Everyone can then verify the signature using a published public key. So long as the private key is secure, and the public key is accepted once by downloaders (with due consideration AND not replaced unless absolutely necessary), then an attacker will find it really difficult to replace that file with a tainted copy. There’s a reason for that, and it resembles the benefits of TLS pinning: you are reducing the number of trusted parties to just one – the trusted author or maintainer of the code. Even when TLS completely collapses, you can still verify the file signature to detect any tampering.

I mentioned it earlier, but Composer is NOT a package manager. The only files that Composer distributes are composer.phar and an installer script. Since it distributes nothing else, it can sign nothing else. It’s a tool that resolves dependencies and then downloads code remotely.

With that in mind there are two parts, if one were to consider file signing support for Composer:

1. Signing composer.phar and the installer; and
2. Signing everything else.

With much of the focus being on securing Composer, the first part raises a few interesting questions. The main one being how to go about establishing trust. Everything boils down to trust and it has to start somewhere. If we think hard about piping code we just downloaded through the PHP interpreter, there’s the problem of explaining how this is wrong. Is it? Let’s say the installer were signed. It sounds secure, but now we’ve just managed to shift the problem elsewhere: we have to download an initial copy of the public key. How does one sign a public key? If we trust the public key, and verify the installer using it, and an attacker has replaced both, we’ve effectively achieved…nothing. In other words, you’re left with the original problem – it’s your problem.

The flaw in Composer’s installer isn’t that it’s unsigned, it’s that it doesn’t afford the opportunity for the downloader to read it before it gets piped to PHP. It’s a documentation issue. You can go down the route of using a CA, of course, but that’s further down the rabbit hole than may be necessary.

Signing the composer.phar file is another matter. In theory, in an ideal world, you’re going to use the installer precisely once. Once you have a copy of composer.phar, you can just distribute and reuse it locally. You wouldn’t be downloading the installer hundreds of times, providing hackers with hundreds of opportunities to attack you. You’ll be copying composer.phar around and using self-update hundreds of times. Signing composer.phar therefore makes sense. That first copy can enforce signature verification on each subsequent update using the original copy of the public key. It limits the available victims of an attack to a significant degree with the bonus of unverifiable signatures creating an immediate early warning system should a compromise at the server or TLS level ever occur.

So the fuss over the installer isn’t entirely well placed. Whether it’s the installer, or a public key, that first download will be unsigned anyway. It’s the subsequent downloads that you need to watch. That will probably be my next PR looking for review. It is possible to implement an encapsulated signature verification check for phar files using openssl.

Signing Everything Else

In much the same way that one may not trust downloads from, there are two other broad sources for downloaded files: and The Planet Earth. I’m going to examine the second, but it’s worth noting that, after all else is done, at some point we will download packages.json. This demonstrates another issue with signing things. If we’re assuming that the server has no access to the private key (since an attacker would love to see that), we have to assume that any automated packages.json updates will be unsigned. Yet…this file provides all of the package metadata consumed by Composer. Let your brain cells chew on that.

Another day, another blog post…

Back to global downloads: If we don’t trust Composer files, why should we trust anyone else’s files? They are all going to lead to PHP executing downloaded code. This is the wonderful realm of package signing. You can see this in action for Debian apt (also Ubuntu…obviously). It’s a simple setup that PHP could borrow. I say PHP, because Composer is not a package manager so whether you sign anything or not is not its call though it could perhaps support the architecture so it’s actually possible.

One method is very straightforward. In a manifest file, you list all files to be downloaded with their matching checksums, i.e. the calculated hash for the file. If a file is altered, its checksum will change. Rather than sign each file (which would be ridiculous), you just sign the manifest. Upon download, the client can verify the signature of the manifest and trust its contents (or reject it if it doesn’t verify!). Once trust is established, the client can then verify that all files downloaded are a) listed in the manifest and b) have a matching checksum.

This could be implemented via git so it’s already compatible with tagged release ZIP downloads, under the assumption that tagged releases would be signed. For dev-master, well, you have to takes risks sometimes I suppose. You can’t expect something like Zend Framework to sign each and every commit. Using the master of a git repo is a risky affair.

Enhanced by Zemanta