Security precautions before you start using PHP on your Apache server, step by step
Introduction - what is PHP?
PHP is a server side scripting language. You can embed PHP code in your web
pages along with HTML. When your server receives a request for a page, it first gives
the page to the PHP handler program. The PHP handler outputs HTML code as-is, but
when it encounters PHP commands, it executes them. Any HTML generated by the PHP
commands is also output. The end result is a web page with content that has been
customized on the server before being sent to whoever requested it.
PHP has capabilities that make it a potential security risk:
- It can receive and process data from the "outside world"
- It can be programmed to actively fetch data from anywhere on the internet
- It is able to read and write files on the server
If you have a website, you can expect to be under constant attack from robots attempting to a) "send in" malicious data and
scripts from the outside world, b) trick your server into fetching malicious scripts and running them, c) read and write files on
your server. Their goal is to take control of your site and use it for their own purposes.
This article gives configuration settings for PHP and rules for PHP coding that are effective at blocking the most common types of
attacks.
The configuration lines are few and the rules are simple.
There are two files where PHP configuration commands can go: php.ini or
Apache
.htaccess.
You should use php.ini as your first choice, if you can. You should be able to use it if:
- You are on
shared hosting at a webhost that uses suPHP or any other configuration that allows individual users to create their own
php.ini files. If in doubt, ask them.
- You are on dedicated hosting (your own rented server), or
- You host your
own website on your own server.
1a) Configure PHP settings with a php.ini file
php.ini specifies the configuration settings PHP will use
when it is running on your website. It determines what things PHP scripts are allowed to do and what they are prohibited from doing.
The following settings affect security.
In your public_html (the same folder where your site's main home page is), create a text file called php.ini that contains these
lines. Instructions for customizing the text shown in red are farther down this page:
allow_url_fopen = Off
display_errors = Off
display_startup_errors = Off
log_errors = On
error_reporting = E_ALL
error_log = /home/yourUserID/public_html/phperr.txt
expose_php = Off
magic_quotes_gpc = On
magic_quotes_sybase = Off
register_globals = Off
Explanations:
These explanations are brief.
This page of the PHP Manual has links to more.
allow_url_fopen = Off
allow_url_fopen = Off is especially important.
It prevents URLs (internet addresses) from being used in PHP include() statements and in some other
places. A command such as include("http://website.com/page.php")
will not be allowed to execute. Only files that reside within your website can be included,
and you must refer to them by their filepath names, not by their internet URLs. You won't be
able to include a file from a different server, but neither will anybody else.
When someone else does it maliciously by embedding the URL in an otherwise innocent-looking HTTP request and hoping that your
script can be tricked into including and running their script, it's called a Remote File Inclusion (RFI)
attack. Having allow_url_fopen = Off dooms all such attacks to fail.
Some webmasters think they need to have allow_url_fopen = On because their pages are already coded to use URLs to include files
from their own site or from some external site. It is worth expending some effort to try to stop doing that so that you can turn allow_url_fopen off:
- You can include a file from your own site simply by specifying its path and filename. Here is an example how to
convert a URL include to one that does not use a URL:
Assume your current code looks like this:
include('http://yoursite.com/page.php');
You would convert it to this:
include($_SERVER['DOCUMENT_ROOT'] . '/page.php');
$_SERVER['DOCUMENT_ROOT'] is a superglobal variable calculated by the server to be the root folder of your site, the
equivalent of "/", which is usually public_html. Note that it does not provide a trailing "/", so you must provide a
leading "/" in '/page.php'. Now you have a reliable method to refer to any file without having to use relative paths and
without using a URL unnecessarily.
- If you include static content (that doesn't change) from another of your websites, such as
include('http://myothersite.com/includes/footer.php'),
you can make a copy of that content in
the current site and then include it locally as described above. Having duplicate copies of a few files is a small price to pay for the
better security of having allow_url_fopen Off.
- If you cannot avoid it and must include content from a remote site using URLs, you'll need to set allow_url_fopen =
On. You can still get some protection from RFI attacks by using .htaccess to ban incoming
requests that contain potentially malicious URLs. See Section 1b) below and follow the link there.
- You can also prevent RFI, without needing special configuration settings, by writing your
PHP scripts to carefully check incoming data to make sure it is not malicious. How to do that
is described
here.
display_errors = Off
display_startup_errors = Off
log_errors = On
error_reporting = E_ALL
These specify that all errors and warnings will be logged to your error
log text file. NO errors or warnings will be displayed on any web page that is sent
out from your server. Errors should never be displayed publicly because they can help someone figure out how to attack your server.
Remember to check your error log when you are testing new code.
error_log = /home/yourUserID/public_html/phperr.txt
This defines the path and file to which your PHP errors and warnings are
logged. Change yourUserID to the cPanel or other
UserID assigned to you by your webhost. The filename can be anything you want. The path
starting with /home seems to be a
common one on Linux servers, but it might vary depending on your webhost. If the above
doesn't work, ask them what it should be. public_html is only shown above as an
example of where it fits in the path. You don't have to put your error log inside public_html. See below.
I recommend using a text file for error logging (as shown above), and not using the
"system log" option that you might see mentioned. Your text file will accumulate errors indefinitely until you empty it,
while the Apache system log can be flushed unpredictably.
Your error log file should be in an area of your webspace that is not publicly
accessible. Any one of these methods will protect it:
- Put it in a folder that is not inside /public_html.
- Protect it with an .htaccess file that prohibits web access, like
this:
In cPanel > File Manager, create the folder.
Inside the folder, create a file called .htaccess (with the leading period).
Put this text in the file:
order allow,deny
deny from all
Save the file.
Test it by trying to go to http://yoursite.com/foldername/
You should get a 403 Forbidden error page.
- Put it in a folder that is inside /public_html, but apply password protection to the folder. You can do this in cPanel.
With A) and B), you can only view the file with cPanel > File Manager or FTP, not by
browser. With C), you can view the file in your browser by entering the password.
expose_php = Off
Not particularly important, but it doesn't hurt. The headers that accompany outgoing pages will not reveal that PHP is running
or its version.
magic_quotes_gpc = On
The PHP manual recommends setting this to Off, and dealing with quotes in a
secure manner on your own, but we're assuming you don't know how to do that yet,
and that you also don't actually have any need yet for the situations it
addresses, so for now the best setting is On.
magic_quotes_sybase = Off
Another special setting of "magic quotes". This should be Off.
register_globals = Off
register_globals = Off is especially important.
You've probably seen URLs that look like
this: http://site.com/index.php?something=somevalue. When register_globals is On, the
variable called something is passed into your script with its value set
to somevalue. When
register_globals is Off, variables passed in like this are not automatically dumped
into your script's variable list. This makes it harder for someone to inject
their own code.
safe_mode = Off
This setting is not in the "recommended php.ini" above. I only mention it
because you might run across it and wonder what it is and how it should be
set. It restricts the permissions
with which PHP scripts run. However, some very popular third party scripts, which you
might want to use eventually, will not run properly when it is set to On. In addition, if your webhost uses suPHP, safe_mode serves no purpose. Lastly,
beginning with PHP 6, safe_mode doesn't even exist. Therefore, it is best left out of your
php.ini file, or, if present, set to Off.
1b) Alternative: configure PHP in .htaccess
If your webhost does not allow you to create your own php.ini, you can
put configuration commands in .htaccess instead. Unfortunately, not all php.ini commands have .htaccess equivalents,
but some of them do.
Put the following lines in a part of your public_html/.htaccess file that is
not delimited by HTML-style tags such as the <Files></Files> tags in the
example in Section 3.1 below. The following lines have the same effects as their
php.ini counterparts described in Section 1a) above, but notice that the format of the commands is different:
php_flag display_errors Off
php_flag display_startup_errors Off
php_flag log_errors On
php_flag magic_quotes_sybase Off
php_flag magic_quotes_gpc On
php_flag register_globals Off
php_value error_log /home/yourUserID/public_html/phperr.txt
php_value error_reporting
2147483647
The most important is the register_globals
line.
It is very unfortunate that allow_url_fopen = Off
has no .htaccess counterpart (although it will in PHP6). Because you cannot stop PHP from reading a file
from a remote server, you need to make sure requests that try to do that maliciously are blocked so they cannot
reach the PHP processor. A previous article has instructions
how to use .htaccess to block
requests that might be Remote File Inclusion attacks.
The following two also cannot be set in .htaccess, but they are unimportant:
expose_php = Off
safe_mode = Off
1c) What if the above configuration settings don't work?
If you ever write code that won't run properly with the above settings, you are leaving behind your "beginner" status and need
to study PHP security more carefully before you go any further.
The Security section of the PHP Manual is one reference that should be
read at some point, but it isn't easy
and probably isn't the best place to start.
2) Viewing your PHP settings
Your server can give you a complete report of all your PHP settings.
- Create a text file with a .php extension, containing just this line:
<?php phpinfo(); ?>
- Upload it to your server into (preferably) a password protected folder.
- Open your browser and type the path into the address bar:
http://yoursite.com/whatever/filename.php.
- Enter your username and password to enter the protected folder and view
the result page.
- Save or print the result page to your local computer for reference.
- Delete the .php file from your server. It is not good to leave
this file lying around where somebody else can run it and see how your server is configured.
3) Modify your .htaccess file
- Important if you use php.ini: In your public_html/.htaccess file, add the following lines if they are
not already there. Reference information for these lines is in the
Apache manual section for mod_access:
# This denies all web access to your php.ini
file.
<Files php.ini>
order allow,deny
deny from all
</Files>
- Required when using suPHP: If your webhost uses suPHP, you will need to enter a
suPHP_ConfigPath line that tells PHP the location of your php.ini file. It will look something like this, but if this doesn't
work, ask your web host what the line should be. Put this in a part of the
file that is not between HTML-style tags like the <Files></Files>
tags in the paragraph above:
suPHP_ConfigPath
/home/yourUserID/public_html
- Optional: If you are adding PHP code to your existing .html files and don't want
to rename them all to .php, you can instruct the server to send all .html
files through the PHP processor as if they had .php extensions. To do
that, add one of these lines into .htaccess, in a part of the file that is
not
delimited by tags. If one doesn't work, try another; experiment.
If you are using suPHP:
AddHandler x-httpd-php .htm .html
If you are not using suPHP:
AddType application/x-httpd-php .htm .html
Apache 2 without suPHP:
AddHandler application/x-httpd-php .htm .html
4) One more php.ini setting, advanced...
The settings list in Section 1a) was supposed to be as simple as possible,
usable by anyone, with a minimum of effort required, but there is one more
php.ini setting that's worth using if you can. Here is an example of its use,
with a list of some of the functions that could be disabled for increased
security:
disable_functions =
exec,shell_exec,passthru,system,eval,show_source,proc_open,
popen,parse_ini_file,dl,(comma-separated list of function names)
This tells PHP not to allow the listed functions to be executed by any script in your site. The functions listed above are
especially powerful, and many malicious scripts use them. By blocking their use, you block the scripts from
causing much of their damage even if they do somehow manage to get into your site and run.
However, some of these functions are used by popular third party PHP scripts
such as forums, blogs, galleries, and shopping carts, so the reason I call this
an "advanced" setting is that before using this line you must search all the PHP
code in your site to make sure you don't disable functions that your site
requires. Nevertheless, disabling functions you don't use is worthwhile if you
don't mind doing the research.
disable_functions is for php.ini
only. It has no .htaccess equivalent.
5) Memorize these best coding practices
Beginner Rules:
- Never use the PHP eval() function.
- Never use PHP to connect to a database. (What this means is that while you are a beginner, you should not
connect to a database at all, until you have studied the hazards of "SQL injection" attacks and understand how to
write
your code properly to guard against them.)
- Never use the $_GET, $_POST, $_COOKIE, $_REQUEST, or $_FILES
variables. These bring data into your script from the outside world. The data has the potential of
being malicious.
Advanced Rule:
When the time comes that you need to break one of the Beginner Rules,
first do a web search on:
PHP security and
spend a few days practicing proper coding techniques for the
methods and functions you plan to use.
6) Things to do
If necessary, email your web host. Ask these questions, and then modify your php.ini
or
.htaccess file as needed:
- Am I allowed to have my own php.ini file?
- I want to create an error log for PHP. Are these paths ok?
php.ini : error_log =
/home/yourUserID/public_html/phperr.txt
.htaccess:
php_value error_log /home/yourUserID/public_html/phperr.txt
- Does my server use suPHP? If so, is this the correct line for .htaccess?
suPHP_ConfigPath
/home/yourUserID/public_html
7) You're ready to start. Good luck and have fun.
Additional security precautions for protecting your website are at
How to prevent your
website from being hacked. How to repair a damaged site.
If you have questions, feel free to ask in the
discussion forum.
|