Sunday, October 16, 2011

Chroot PHP-FPM and Apache

As mentioned in "A Note on Security in PHP", the PHP security features (safe_mode, open_basedir, disable_functions) can be bypassed. The Stefan Esser’s paper also describe how to bypass the PHP security features. The better alternative for PHP security is chroot PHP-FPM.

With google, you can easily find how to configure PHP-FPM for nginx. But I want the setup for Apache httpd. I found only this two "Install Drupal in php-fpm (fastcgi) with Apache and a chroot php-fpm" and "The Perfect LAMP Stack – Apache2, FastCGI, PHP-FPM, APC". They explain very well for configuring fastcgi and PHP-FPM. But only first link describe about chroot PHP. The method to chroot is somewhat ugly. Why do I have to create a symlink?

After reading Apache and PHP doc, I found the options. We just need to set "doc_root" to a new web path after chrooted and "cgi.fix_pathinfo" to 0 in "php.ini". We can also set these options per PHP-FPM pool with "php_admin_value" directive.

Updated on 11 Aug 2012

Note: I just notice the _SERVER variables related to path are wrong if "cgi.fix_pathinfo" is 0. If PHP application relies on these variables (such as _SERVER["SCRIPT_FILENAME"], $_SERVER["PATH_TRANSLATED"]), it would fail.

Another method is patching PHP-FPM. Here is my quick and dirty patch for PHP 5.3.15 I added "cgi.fix_chrootpath" configuration. Just set it to the same value as "chroot" value in pool configuration. Do not set "doc_root" and "cgi.fix_pathinfo". The "cgi.fix_chrootpath" should be boolean. But I cannot find a method to access "chroot" pool configuration. Last, I did not test the patch much. It works on my Linux box.

Also do not forget to remove "FollowSymLinks" or add "SymLinksIfOwnerMatch" option in Apache httpd configuration. If you omit it, the attacker can use symlink() trick to read files that web user can read.

After doing chroot PHP, you might think the open_basedir and disable_fuctions are useless. In my opinion, open_basedir is still useful. They can prevent from PHP functions to read files from "upload_tmp_dir" and "session.save_path". So attacker cannot use "temporary upload file" and "session file" for LFI.

Last thing that only few people mention, noexec mount option can be used for web data if web application use only PHP.