Saturday, August 7, 2010

Hardening PHP: How to securely include remote code (part 3)

In this last post of the series we learn how to use Phar archives and OpenSSL together to build a secure remote code deployment framework. I present PharUtil - the library adding convenience and security to Phar functionality.

Previously on blog

Last time I talked about using Public key cryptography to digitally sign a PHP code. You can sign the code with you private key and anyone later on could verify that the code is yours by checking the signature with your public key (that you need to make available somewhere).

Code signing solves the problem of secure remote code deployment. Going back to our client-consumer & server-producer architecture:


Server having the private key and the code, packages up the code as Phar archive and signs it. Client, having the public key, downloads the code from the server, and, to be sure that it's authentic, verifies it. If the code passes signature verification, it can be safely used.

While Phar archives solved the problem of packaging the code and signing/verifying it, we were only missing the final glue - Phar doesn't work remotely, so there is no "connection" between server and client.

The missing piece

Now is the time to introduce the missing piece: the PharUtil library.
PharUtil library is:
  • a set of command line utilities for working with Phar
  • PHP class that downloads and verifies the remote Phar archive
With PharUtil secure code download is that simple:
// all verified Phars will be copied to lib/ directory
$verifier = new PharUtil_RemotePharVerifier('/tmp', './lib', './cert/public.pem');
try {
  $verified_file = $verifier->fetch("http://example.com/library.phar");
} catch (Exception $e) {
 // verification failed
 exit();
}

// $verified_file contains absolute filepath of a downloaded file
// with signature verified from './cert/public.pem'
include_once $verified_file;
// or
include_once 'phar://' . $verified_file . '/some/file/within.php';
// or
echo file_get_contents('phar://' . $verified_file . '/readme.txt');
?>
Backstage:
  1. A Phar file is downloaded to /tmp 'staging' directory 
  2. Local public key is attached to the Phar
  3. A public key is checked
  4. Phar is validated to be OpenSSL signed with the given public key
  5. Phar file is moved to lib/ directory to be used by application
When working on PharUtil, I discovered many quirks with Phar that affect security (e.g. Phar compression silently downgrades OpenSSL signature to SHA signature, dummy public key file makes all archives valid etc.) - all of them are being addressed in PharUtil, so you get an Even-Safer-Phar.

If you don't want the whole signing thing and just need to download Phar and check if it has no transfer errors - I'm fine too, just don't pass the third parameter (public key) to the constructor.

Bonus

PharUtil gives you a few useful command-line utilities to deal with Phar archives.
  • phar-build for building and signing Phar archives
    (see phar-build -h for help)
  • phar-extract for extracting/listing contents of Phar archive
    (see phar-extract -h for help)
  • phar-verify for verifying signature of Phar archive
    (see phar-verify -h for help)
  • phar-generate-cert for generating OpenSSL certificates used to sign the Phar archives (OpenSSL installation is required)
How to use them? See the examples:

Build a Phar archive

Generate certificates in cert/ directory (will be put in priv.pem and pub.pem)
$ mkdir cert/ 
$ cd cert/ 
$ phar-generate-cert 
Create src/ directory and copy all the files to be included in Phar
$ phar-build --phar library.phar

Extract a Phar archive

$ phar-extract library.phar output-directory

Verify a Phar archive signature

$ phar-verify -P pub.pem http://example.com/library.phar
(this is using PharUtil_RemotePharVerifier internally :))

List a Phar archive contents

$ phar-extract -l library.phar output-directory

Download

To install the library use my PEAR channel:
$ pear channel-discover pear.kotowicz.net
$ pear install kotowicz/PharUtil-beta
To read more and/or look at the source code - head over to github.com/koto/phar-util

Summary

In this post series, I talked about PHP remote code execution vulnerability and showed that the cause of it is, well, remote code that we can't verify. Then we went through different methods of dealing with the remote code and, finally, have found a way to sign the PHP code and be able to securely distribute it to remote clients.

We've come a long way since a few years ago and now we, as a PHP community, have the possibility to maintain security AND include remote signed code in our applications. And it's a low-hanging fruit thanks to OpenSSL, Phar and, now, PharUtil library. Code signing is here - now we just have to use it in our next killer distributed apps.

4 comments:

  1. Hi:
    I think that the file php-build has a bug but I didn't know where to fill a request:

    // buildFromIterator unfortunately sucks and skips nested directories (?)
    foreach ($iterator as $file) {
    echo "adding " . $file . PHP_EOL;
    if ($file->isFile()) {
    $phar->addFile($file, str_replace($options['src'], '', $file));
    }
    if ($file->isDir()) {
    // this also doesn't work :(
    $phar->addEmptyDir(str_replace($options['src'], '', $file));
    }
    }

    This piece of code will crash on a linux system because $file is not taking care about the '.' and the '..' listed in the interator, so as a patch I manage to handle it with

    if(substr($file, -1) === '.') continue;

    Thanks for this great job.

    ReplyDelete
  2. @chevrox

    Thanks for reporting, I fixed the issue (although there was no such error on my linux box) in v0.5.3, update using:

    $ sudo pear upgrade kotowicz/pharutil-beta

    For future issues please use http://github.com/koto/phar-util/issues webpage or mail me directly (I will add contact deatils on 'about me' page now)

    ReplyDelete
  3. Hello.
    Have you ever tried running Drupal7 as a PHAR archive. If so, do you have any pointers or gotchas?
    The objective is to create Drupal website as a PHAR Archive and dropped into the web server's root directory and run from there as a regular site. This would help deployment of updates to the application as I would have to just replace the current application with another PHAR archive

    ReplyDelete
  4. No, sorry. This post is 4 years old, I no longer do PHP.


    2014-02-18 21:32 GMT+01:00 Disqus :

    ReplyDelete