The Apache/PHP/MySQL stack is immensely popular for web application development. Its components are powerful, versatile and Free. Unfortunately however, PHP comes with a default configuration that is not suitable for production mode, and may cause developers to use insecure techniques during the development phase. Inside is a check list of settings that are intended to harden the default PHP installation.
Disabling Remote URLs for File Handling Functions
File handling functions like fopen, file_get_contents, and include accept URLs as file parameters (for example: fopen('http://www.example.com/', 'r')). Even though this enables developers to access remote resources like HTTP URLs, it poses as a huge security risk if the filename is taken from user input without proper sanitization, and opens the door for remote code execution on the server. To disable this and limit file functions to local system, use the following setting in php.ini:
allow_url_fopen = Off
Network resources will still be accessible through fsockopen or CURL functions.
Most of the following settings are also located in the PHP configuration file php.ini. Its actual path depends on your OS. You may use the search feature to locate it if you don't know where it is already.
Register Globals
Prior to version 4.2.0, PHP used to provide input values as global variables. This feature was named register_globals, and it was responsible for many security issues in web applications because it allowed attackers to freely manipulate global variables in many situations. Fortunately it's disabled by default from PHP 4.2.0 and on, because it's dangerous on so many scales. Do not enable it no matter what. If some script requires it then the script is most likely insecure. If a developer requests it to be enabled, then they are very likely to be incompetent. Don't listen to them and keep it off!
register_globals = Off
Restricting What PHP Can Read and Write
More often than not, PHP scripts only need I/O access to a certain subdirectory in the filesystem, /var/www/htdocs/files for instance. In this case, you can limit what fopen and other file access functions can read and write to by using the following directive:
open_basedir = /var/www/htdocs/files
Safe Mode
PHP has a safe mode. In this mode, access to files not owned by Apache is disabled, and access to environment variables and execution of binary programs are also disabled.
In its default state, PHP's safe mode is too restrictive for any advanced development to be possible. However, there are several settings to relax it. The biggest problem with safe mode is that only files owned by Apache are accessible to PHP scripts. This is often impractical when many developers are working on the same project, or when you want PHP to read a file without changing its ownership. Another affected situation is when you want PHP to read files generated by other programs. To work around this, there is a setting that checks for file group instead of owner:
safe_mode = Off safe_mode_gid = On
With safe_mode_gid enabled instead of safe_mode, PHP will be able to open files that belong to Apache's group regardless of the owner. So if there are several developers working on the same server, add them to Apache's group, make it their default group, and everything should be set.
Safe mode is also useful in stopping PHP from executing binaries, but sometimes you may need to let it run specific programs. In this case place these binaries (or symbolic links to them) in a directory (/var/www/binaries for instance) and use the following option:
safe_mode_exec_dir = /var/www/binaries
Finally, to allow access to certain environment variables, use the following setting, providing a comma-separated list of prefixes. Only environment variables which names begin with one of the prefixes will be accessible:
safe_mode_allowed_env_vars = PHP_
Posing Limits
It's always a good idea to put limits on PHP's execution time, memory usage, POST and upload data. To do this, use the following self-explanatory options:
max_execution_time = 30 ; Max script execution time max_input_time = 60 ; Max time spent parsing input memory_limit = 16M ; Max memory used by one script upload_max_filesize = 2M ; Max upload file size post_max_size = 8M ; Max post size
Needless to say, you may tweak the values to suit your needs.
Limit Access to Certain File Name Patterns
Many file extensions should not be accessible by end users. Take for example .inc. Some developers prefer to assign this extension to included scripts. The problem here is that this extension isn't parsed by the PHP engine, and as a result, anyone can view the source code by requesting the file itself: http://www.example.com/includes/settings.inc
Such files may contain sensitive data like MySQL passwords. So you need to ensure that end users can not access those files. Other candidate extensions are .sql, .mysql, and .pgsql.
Another pattern to look out for is backup files. Some editors create backup versions of edited files in the same directory where the original file is located. For example, if you edit index.php, a backup called index.php~ will be created. Given that this file doesn't end with .php, it will not be processed by the PHP engine, and its code will also be available to users by requesting http://www.example.com/index.php~
To avoid the risks mentioned above, you can use the following Apache directive:
<filesmatch> Order allow,deny Deny from all </filesmatch>
Place it in a .htaccess file or in Apache's configuration. Adding more file extensions should be trivial to those familiar with regular expressions.
Error Messages and Logging
By default, PHP prints error messages to the browser's output. While this is desirable during the development process, it may reveal security information to users, like installation paths or usernames. It's highly recommended to disable this on a production server, and send error messages to a log file instead:
display_errors = Off log_errors = On
Hiding The Presence Of PHP
PHP reveals its presence on the server in a variety of ways: It may send an HTTP header (X-Powered-By: PHP), or append its name and version to Apache's signature. In addition, there are easter egg URLs that return the PHP logo, one of them is: http://www.example.com/script.php?=PHPB8B5F2A0-3C92-11d3-A3A9-4C7B08C10000
Obviously there is no reason to let end users know about the server's PHP version. Luckily, there is a switch in php.ini that will disable all of the above:
expose_php = Off
Summary
This is a quick summary of the settings mentioned above. It can be used as a reference when configuring a newly-installed Apache server:
; php.ini allow_url_fopen = Off ; Disable URLs for file handling functions register_globals = Off ; Make sure this hellish fiend is dead open_basedir = /var/www/htdocs/files ; Restrict file handling functions to a subdirectory safe_mode = Off ; Disable this, the next is often more practical safe_mode_gid = On ; Enable safe mode with group check safe_mode_exec_dir = /var/www/binaries ; Restrict execution functions to this directory safe_mode_allowed_env_vars = PHP_ ; Restrict access to environment variables max_execution_time = 30 ; Max script execution time max_input_time = 60 ; Max time spent parsing inputs memory_limit = 16M ; Max memory size used by one script upload_max_filesize = 2M ; Max upload file size post_max_size = 8M ; Max post size display_errors = Off ; Do not show errors on screen log_errors = On ; Log errors to log file expose_php = Off ; Hide presence of PHP
# Apache configuration or .htaccess <filesmatch> Order allow,deny Deny from all </filesmatch>
Conclusion
The default configuration that ships with PHP is intended for development purposes. Therefore, it's always advisable to re-configure PHP with security in mind before going into production phase. Some security settings are also recommended during the development phase to prevent programmers from producing vulnerable code, and make them stick to secure techniques.












Comments
nadia
Great tips, keep up the good work ;)
Posted at 12:57 p.m. on October 6, 2006
David Singleton
There's some great tips in there, I would make one point though:
As much as I agree, it's not always possible (in a commercial enviroment at least) to ensure it will be turned off, some shared hosts insist on keeping it on for legacy reasons and it can be hard to convince clients to move to more sensible hosts. Persnally I allways keep in mind it's something that might be on, and code accordingly so that it being on or off doesn't affect me.
Cool tips though, and i've started skimming through the rest of your articles!
Cheers, David
Posted at 10:02 a.m. on October 9, 2006
Ayman Hourieh
Hi,
I'm glad that you're enjoying my articles :)
By the way, it's often possible to disable register_globals for a shared hosting account using .htaccess:
And I agree with you. A security-conscientious developer should assume that this feature may sometimes be enabled and work accordingly.
Thanks for stopping by :)
Posted at 10:20 a.m. on October 9, 2006
corgi
Thank you for those tips; they helped me in preparation of our new machine [current server is such an aging mess]. Greetings and sympathy from Czech Republic :)
Posted at 5:11 a.m. on February 12, 2007
Robert
Thank you for the article Ayman. I see what need to be changed in my php.ini file :)
Posted at 9:47 p.m. on April 17, 2007
Kahuki
Very nice overview! I think it is time to take a look at my php.ini now ;)
Posted at 12:11 p.m. on June 3, 2007
Shahar Evron
Great checklist - very useful.
I would also add some session security tips - you can reduce the risk of session fixation by only allowing session IDs to be passed as cookies. You can do that by setting:
Ma'salame,
Shahar.
Posted at 4:26 p.m. on December 7, 2007
Darko Bunic
Yes, this tips completely cover PHP configuration. But, I would like to add more for Apache configuration. As you suggest to hide presence of PHP, it is also good to hide Apache version and OS from HTTP header also with "ServerTokens Prod" and "ServerSignature Off" directives. Default Apache configuration loads huge list of modules, and there are needless among them - so turn it off and minimize amount of memory per httpd child process. It is good to disable directory browsing for public Web site, add "-" (minus) before Indexes inside document root settings. This Apache tips aren't mystical, but you can harden your LAMP setup even more. I wrote few articles on my www.redips.net and I hope you will find useful informations there. Thank you for great article Ayman
Posted at 8:10 a.m. on August 1, 2008
p3rtinax
hi very useful, i'm translating it to persian(farsi), with copy right ;) :-x thanks
Posted at 11:56 a.m. on October 6, 2008
ilyas
These security stuffs you have posted here are very invaluable to all php programmers. Thanks allot for this.
Posted at 11:04 a.m. on November 3, 2008
Saidur
This is very helpful. Nice Articles !
Posted at 3:42 p.m. on November 12, 2008
Hi, I never read something like this. This is really new for me and thanks But, are software like easyphp or xampp coming with the same default php configuration? thanx
Posted at 12:20 p.m. on January 15, 2009
Peter Hawkes
I'm currently working on a CPanel server for a client that runs both PHP4 and PHP5. PHP5.28 is CGI and by default is loading the PHP4 php.ini so register_globals is on. It is very important to always check your server setup because it is possible to find some very unusual config issues such as this one.
Posted at 9:11 p.m. on March 22, 2009
ilyas
I was searching for this for past couple of days, now i have go and fix my php.ini file...
Posted at 2:44 p.m. on May 31, 2009
Jay
Seems to me these tips should mainly for PHP 4...
Posted at 4:32 a.m. on June 26, 2009
Anton Ongsono
What did max_input_time = -1 means?
Posted at 2:31 p.m. on July 7, 2009
Phil
What about chrooting PHP?
Posted at 9:45 a.m. on September 11, 2009
Taras
great article, man, thanks
Posted at 12:54 a.m. on February 15, 2010
Will Macdonald
I would also suggest this: session.cookie_httponly = True
This will greatly reduce risk of XSS attacks
Posted at 8:12 p.m. on May 6, 2010
Eyal Estrin
Check out my step-by-step guide for hardening PHP 5.3.2 http://eyalestrin.blogspot.com/2010/05/hardening-guide-for-php-532-on-apache.html
Posted at 5:57 p.m. on May 25, 2010
Nikesh
Good checklist on Securing PHP.
Posted at 11:09 a.m. on June 13, 2010
ALA ATRASH
Hello, are these security techniques applicable to PHP 5.x and newer?
Posted at 1:28 a.m. on February 24, 2011
Ajay Kumar Singh
Nice tutorial. I've been managing my own dedicated server and more or less I also use similar configuration. most of the open source php scripts will work with above apache configuration but some of them will not work and you have to tweak.
Posted at 11:55 p.m. on March 16, 2011
Phil
I've written a PHP script to check most of the settings for you:
http://www.idontplaydarts.com/2011/02/hardening-and-securing-php-on-linux/
Posted at 4:15 a.m. on June 25, 2011
Boofus McGoofus
Thanks for this. Almost 5 years old, and still a good jumping off place as I build my pre-deployment checklist.
Posted at 4:55 p.m. on July 22, 2011