GithubHelp home page GithubHelp logo

koto / phar-util Goto Github PK

View Code? Open in Web Editor NEW
130.0 9.0 20.0 313 KB

PharUtil - Security-oriented utilities for Phar archives

Home Page: http://blog.kotowicz.net/2010/08/hardening-php-how-to-securely-include.html

License: MIT License

PHP 97.76% Shell 2.24%

phar-util's Introduction

PharUtil - Security-oriented utilities for Phar archives

Command line utilities for building, signing and verifying Phar archives with OpenSSL public/private key. Additionally this library contains classes that help you distribute the code (e.g. plugins, skins) to remote PHP applications over HTTP and, thanks to signing, remove the risk of Arbitraty Remote Code Execution in those applications.

If you're not interested in remote deployment, you could always use included command line utilities to help you deal with Phar archives.

Author: Krzysztof Kotowicz - kkotowicz at gmail dot com

License: MIT

Source code: github

PEAR channel server: pear.kotowicz.net

Command line utilities

  • phar-build for building and signing Phar archives
  • phar-extract for extracting/listing contents of Phar archive
  • phar-verify for verifying signature of Phar archive
  • phar-generate-cert for generating OpenSSL certificates used to sign the Phar archives
  • phar-file-checksums for generating / checking files checksum to see if Phar archive is up-to-date with source files

All scripts support -h command line parameter to display their usage.

Installation

Dependencies

  • PHP >= 5.2.0
  • OpenSSL compiled into PHP (--with-openssl) - windows users please consult PHP manual
  • Phar 2.0.0

If you're using PHP >= 5.3.0, Phar is already bundled. For older versions you must build it from pecl.

E.g. under Ubuntu, these steps are required to build and enable the Phar extension:

$ sudo apt-get install php5-dev
$ sudo pecl install pecl/phar
$ echo "extension=phar.so" | sudo tee /etc/php5/conf.d/phar.ini

Configuring Phar

To be able to build Phar archives, you need to change php.ini setting: phar.readonly=0. Under Ubuntu, this is done by executing:

$ echo "phar.readonly=0" | sudo tee -a /etc/php5/conf.d/phar.ini

This step is optional (and discouraged) if you will only be reading Phar archives

Install the package

Install the library through PEAR installer:

$ sudo pear channel-discover pear.kotowicz.net
$ sudo pear install kotowicz/PharUtil-beta

Usage

Building 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 build the archive from there
  • Build a signed phar archive $ phar-build --phar library.phar

Extracting a Phar archive

$ phar-extract library.phar output-directory

Verifying a Phar archive signature

$ phar-verify -P pub.pem http://example.com/library.phar

List a Phar archive contents

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

Using Phar archive

Just use it like a normal Phar archive

include_once 'phar://path/to/library.phar';

Using Phar-util in a build script

Thanks to Damian Bushong , Phar-Util now comes with a script used to generate file checksums in Phar archive and compare them with source file checksums to see if a Phar archive needs to be rebuilt. Thanks to using exit codes this can be easily used in your app build scripts. Use phar-file-checksums --help command for instructions.

Secure remote code deployment with PharUtil

Phar archives, though they have many superb features for a PHP developer, have a certain limitation when it comes to their security - although standard Phar file can be signed, the key used to verify the signature (i.e. the public key) must be stored alongside the archive. Moreover, the signature verification process is hardcoded and does not allow you to supply the public key by yourself.

These problems make it difficult to use Phar archives to distribute a trusted code to clients, because the code and public key have to be stored on the server. Attacker could use e.g. DNS spoofing to emulate the server and supply the code without any signature or using his own public key. Because of that, Standard Phar extension does not allow including remote (e.g. HTTP://) Phar archives to avoid the the risk of arbitrary remote code execution (see e.g. allow_url_fopen and allow_url_include discussion).

To overcome this problem, the PharUtil library recommends using a different approach:

  • the key used to verify the signature is stored on a client (shared-secret) and is never transferred over-the-wire
  • all code is downloaded to the local client sandbox and is verifed using the stored key
  • after verification the code can by safely included

For a more detailed view of different methods of including remote code see (and why should you care at all), see Hardening PHP: How to securely include remote code

Usage scenario

With PharUtil, the usage scenario is as follows:

On server (publisher):

  1. Create public / private keys with Open SSL (phar-generate-cert)
  2. On a server build a code (phar-build) and sign it using your private key.

You may use the built archive on a server like any other Phar archive (see example/local.php)

On client (consumer):

  1. Bundle a copy of public key (cert/pub.pem) for signature verification
  2. Download a Phar archive from the server
  3. Verify Phar signature using local copy of public key (to be sure that the code has been generated by trusted entity)
  4. Include and run the Phar archive in your application

4,5,6 - see example/remote.php or the example below

Mentioned files are installed in example subdirectory of PEAR package documentation (e.g. /usr/share/php/docs/PharUtil/example).

Disclaimer: For the security of given method, it is critical to never disclose the private key! This method also doesn't protect anyone from looking AT the code - the code is not encrypted, it is only signed so it cannot be changed by third party.

Using PharUtil_RemotePharVerifier on a client

Use PharUtil_RemotePharVerifier class to securely check for the Phar signature before using the archive.

<?php

// 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');
?>

Contact

Krzysztof Kotowicz - kkotowicz at gmail dot com

phar-util's People

Contributors

cweiske avatar katanacrimson avatar koto avatar metal3d avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

phar-util's Issues

Several test cases fail with errors

Several test cases fail with errors:

phpunit RemotePharVerifierTest.php
PHPUnit 3.7.13 by Sebastian Bergmann.

...EE.......................EEEEE

Time: 0 seconds, Memory: 6.00Mb

There were 7 errors:

  1. PharUtil_RemotePharVerifierTest::testVerifyDoesntCopyToVerifiedDir
    RuntimeException: rmdir(/usr/share/php/tests/PharUtil/test/PharUtil/tmp/tmp/..): Directory not empty

/usr/share/php/PharUtil/RemotePharVerifier.php:182
/usr/share/php/tests/PharUtil/test/PharUtil/RemotePharVerifierTest.php:247
/usr/share/php/tests/PharUtil/test/PharUtil/RemotePharVerifierTest.php:27

  1. PharUtil_RemotePharVerifierTest::testVerifyCorrectlyVerifies with data set #0 ('wrongsig.phar')
    RuntimeException: unlink(/usr/share/php/tests/PharUtil/test/PharUtil/tmp/tmp): No such file or directory

/usr/share/php/PharUtil/RemotePharVerifier.php:182
/usr/share/php/tests/PharUtil/test/PharUtil/RemotePharVerifierTest.php:249
/usr/share/php/tests/PharUtil/test/PharUtil/RemotePharVerifierTest.php:17

  1. PharUtil_RemotePharVerifierTest::testMovingToVerifiedDirectory
    RuntimeException: rmdir(/usr/share/php/tests/PharUtil/test/PharUtil/tmp/verified/.): Directory not empty

/usr/share/php/PharUtil/RemotePharVerifier.php:182
/usr/share/php/tests/PharUtil/test/PharUtil/RemotePharVerifierTest.php:247
/usr/share/php/tests/PharUtil/test/PharUtil/RemotePharVerifierTest.php:27

  1. PharUtil_RemotePharVerifierTest::testInvalidFileWontReachVerifiedDirectory
    RuntimeException: rmdir(/usr/share/php/tests/PharUtil/test/PharUtil/tmp/verified/.): Directory not empty

/usr/share/php/PharUtil/RemotePharVerifier.php:182
/usr/share/php/tests/PharUtil/test/PharUtil/RemotePharVerifierTest.php:247
/usr/share/php/tests/PharUtil/test/PharUtil/RemotePharVerifierTest.php:27

  1. PharUtil_RemotePharVerifierTest::testRenamingAFileStillMaintainsValidation
    RuntimeException: rmdir(/usr/share/php/tests/PharUtil/test/PharUtil/tmp/tmp/..): Directory not empty

/usr/share/php/PharUtil/RemotePharVerifier.php:182
/usr/share/php/tests/PharUtil/test/PharUtil/RemotePharVerifierTest.php:247
/usr/share/php/tests/PharUtil/test/PharUtil/RemotePharVerifierTest.php:17

  1. PharUtil_RemotePharVerifierTest::testIncludingVerifiedFile
    RuntimeException: rmdir(/usr/share/php/tests/PharUtil/test/PharUtil/tmp/verified/.): Directory not empty

/usr/share/php/PharUtil/RemotePharVerifier.php:182
/usr/share/php/tests/PharUtil/test/PharUtil/RemotePharVerifierTest.php:247
/usr/share/php/tests/PharUtil/test/PharUtil/RemotePharVerifierTest.php:27

  1. PharUtil_RemotePharVerifierTest::testVerifyingUsuallyKeepsFilenames
    RuntimeException: rmdir(/usr/share/php/tests/PharUtil/test/PharUtil/tmp/verified/.): Directory not empty

/usr/share/php/PharUtil/RemotePharVerifier.php:182
/usr/share/php/tests/PharUtil/test/PharUtil/RemotePharVerifierTest.php:247
/usr/share/php/tests/PharUtil/test/PharUtil/RemotePharVerifierTest.php:27

FAILURES!
Tests: 33, Assertions: 62, Errors: 7.

phar-build always want to sign phar package

Hi,
I tried to create a phar archive without signing it (using -n or --ns option).

Building Phar archive from home...
PHP Warning:  file_get_contents(./cert/priv.pem): failed to open stream: No such file or directory in /usr/bin/phar-build on line 142
PHP Stack trace:
PHP   1. {main}() /usr/bin/phar-build:0
PHP   2. file_get_contents() /usr/bin/phar-build:142
Error: Could not load private key from './cert/priv.pem'!

Thanks for this great tools :)

phar-build

Hi:
I came accross another issue using this tool in the same block of code:
// 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() && !$iterator->isDot()) {
// this also doesn't work :(
$phar->addEmptyDir(str_replace($options['src'], '', $file));
}
}
}
the thing is that if you use this code against a dir structure like a/b/a/c.php then the str_replace will replace both "a" giving back something like /b//c.php and the build will fail...
so my propose about this is using the ltrim function that ensures the strip just from the beginning of the string,
also a suggest about this piece of code is that the check on the !$iterator->isDot() is made in the entire block of code so we don't get the adding a/b/c/. and a/b/c/.. and we avoid the entire process on that.
So the proposed piece of code:
foreach ($iterator as $file) {
if (!$iterator->isDot()){
echo "adding " . $file . PHP_EOL;
if ($file->isFile()) {
//$phar->addFile($file, str_replace($options['src'], '', $file));
$phar->addFile($file, ltrim($file,$options['src']));
}
if ($file->isDir() ) {
// this also doesn't work :(
// $phar->addEmptyDir(str_replace($options['src'], '', $file));
$phar->addEmptyDir(ltrim($file,$options['src']));
}
}
}

again thanks for this job, :D

Comment stripping from files

Should look into stripping comments from files before they're stored in the phar. While I'm not sure how exactly this could be implemented, I do know that Silex, the Symfony2 microframework https://github.com/fabpot/Silex, does this itself.
If used, it should trim down the size of the phar file substantially if the files have a healthy amount of inline documentation; after all, those lines aren't needed since it's being packaged into a phar.

Anways, this seems to be implemented using a method defined in the Symfony kernel, looking at this function: https://github.com/fabpot/Silex/blob/master/src/Silex/Compiler.php#L70
Which, in turn, apparently uses a giant loop over token_get_all()
(documentation: http://us3.php.net/manual/en/function.token-get-all.php)

Consider this a feature request, I guess.

If I get the chance in a few weeks/months, I'll see if I can work out implementation myself but right now I'm pressed for time as I've got several papers to do for english, a speech to hammer out, and exams on the horizon.

Can't create self-executable phar.

Hi!

I have a great need to create self-executable Phars...

I need the ability to add the following to the very top of the generated phars:

#!/bin/env php
<?php

Can you please add this support via a command line argument to phar-util?

Thanks!

Verify public key

In here why don't we just verify the public key before trying to use it?

        // When public key is invalid, openssl throws a
        // 'supplied key param cannot be coerced into a public key' warning
        // and phar ignores sig verification.
        // We need to protect from that by catching the warning

I think openssl_pkey_get_public($certificate) would do the job. So this is an input validation task, which should be in the setter and not in the processing code as some kind of workaround...

Btw why don't you send an issue about this feature. Maybe phar maintainers add it to the next release. (it is weird to talk about libs which haven't have maintenance for such a long time)

phar-build encounters fatal error after commit 1865248c18704731b55302b4044e7614136629cd

Upon executing phar-build:
obsidian@lithion-mint ~/code/crcverify $ phar-build --phar verifier.phar
PHP Fatal error: Call to undefined method SplFileInfo::isDot() in /usr/bin/phar-build on line 161
PHP Stack trace:
PHP 1. {main}() /usr/bin/phar-build:0
phar-build 0.5.3

Building Phar archive from ./src...
adding ./src/bootstrap.php
adding ./src/vendor
PHP Fatal error:  Call to undefined method SplFileInfo::isDot() in /usr/bin/phar-build on line 161
PHP Stack trace:
PHP   1. {main}() /usr/bin/phar-build:0

uname:
obsidian@lithion-mint ~/code/crcverify $ uname -a
Linux lithion-mint 2.6.32-21-generic #32-Ubuntu SMP Fri Apr 16 08:09:38 UTC 2010 x86_64 GNU/Linux

PHP info:
obsidian@lithion-mint ~/code/crcverify $ php -v
PHP 5.3.2-1ubuntu4.5 with Suhosin-Patch (cli) (built: Sep 17 2010 13:49:46)
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies
with Xdebug v2.0.5, Copyright (c) 2002-2008, by Derick Rethans

If you need additional info, just ask.

Phar with Encryption

Hi,

First thanks for the great tool!
Wow!

I'm defiantly going to use it.

I was wonder if you also have an idea or other solution how to encrypt the PHAR file so it could not be read as php code. So even I extract the PHAR file, I will have nothing.

I know about Zend Guard and ionCube that can encrypt your code, but I'm not sure if they support PHAR, in such a way that files inside the PHAR can be encrypted.

My goal is to distributed my php code, which should run on remote clients and make it secure as possible. The solution you develop here is super coool and save me almost 90% of the work, but I'm still missing the option to decode/decrypt the source files.

I have check ionCube and they seems to support PHAR, so I might be able to do so.
but I also going to check bcompiler.

Was wondering if u have any idea how to create a "LOCKED" or phar archive.

Thanks
Sassy

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.