Learn how to improve your security a little further with the
second part of this PHP tutorial.
In Writing Secure PHP, I covered a few of the most common security holes in websites.
It's time to move on, though, to a few more advanced techniques for securing a
website. As techniques for 'breaking into' a site or crashing a site become
more advanced, so must the methods used to stop those attacks.
File Systems
Most hosting
environments are very similar, and rather predictable. Many web developers are
also very predictable. It doesn't take a genius to guess that a site's includes
(and most dynamic sites use an includes directory for common files) is an
www.website.com/includes/. If the site owner has allowed directory listing on
the server, anyone can navigate to that folder and browse files.
Imagine for a second
that you have a database connection script, and you want to connect to the
database from every page on your site. You might well place that in your
includes folder, and call it something like connect.inc. However, this is very
predictable - many people do exactly this. Worst of all, a file with the
extension ".inc" is usually rendered as text and output to the
browser, rather than processed as a PHP script - meaning if someone were to
visit that file in a browser, they'll be given your database login information.
Placing important
files in predictable places with predictable names is a recipe for disaster.
Placing them outside the web root can help to lessen the risk, but is not a
foolproof solution. The best way to protect your important files from
vulnerabilities is to place them outside the web root, in an unusually-named
folder, and to make sure that error reporting is set to off (which should make
life difficult for anyone hoping to find out where your important files are
kept). You should also make sure directory listing is not allowed, and that all
folders have a file named "index.html" in (at least), so that nobody
can ever see the contents of a folder.
Never, ever, give a
file the extension ".inc". If you must have ".inc" in the
extension, use the extension ".inc.php", as that will ensure the file
is processed by the PHP engine (meaning that anything like a username and password
is not sent to the user). Always make sure your includes folder is outside your
web root, and not named something obvious. Always make sure you add a blank
file named "index.html" to all folders like include or image folders
- even if you deny directory listing yourself, you may one day change hosts, or
someone else may alter your server configuration - if directory listing is
allowed, then your index.html file will make sure the user always receives a
blank page rather than the directory listing. As well, always make sure
directory listing is denied on your web server (easily done with .htaccess or
httpd.conf).
------
Out of sheer
curiosity, shortly after writing this section of this tutorial, I decided to
see how many sites I could find in a few minutes vulnerable to this type of
attack. Using Google and a few obvious search phrases, I found about 30 database
connection scripts, complete with usernames and passwords. A little more
hunting turned up plenty more open include directories, with plenty more
database connections and even FTP details. All in, it took about ten minutes to
find enough information to cause serious damage to around 50 sites, without
even using these vulnerabilities to see if it were possible to cause problems
for other sites sharing the same server.
-----
Login Systems
Most site owners now
require an online administration area or CMS (content management system), so
that they can make changes to their site without needing to know how to use an FTP client. Often, these are placed in predictable locations (as covered
in the last article), however placing an administration area in a hard-to-find
location isn't enough to protect it.
Most CMSes allow users
to change their password to anything they choose. Many users will pick an
easy-to-remember word, often the name of a loved one or something similar with
special significance to them. Attackers will use something called a
"dictionary attack" (or "brute force attack") to break this
kind of protection. A dictionary attack involves entering each word from the
dictionary in turn as the password until the correct one is found.
The best way to
protect against this is threefold. First, you should add a turing test to a login
page. Have a randomly generated series of letters and numbers on the page that
the user must enter to login. Make sure this series changes each time the user
tries to login, that it is an image (rather than simple text), and that it
cannot be identified by an optical character recognition script.
Second, add in a
simple counter. If you detect a certain number of failed logins in a row,
disable logging in to the administration area until it is reactivated by
someone responsible. If you only allow each potential attacker a small number
of attempts to guess a password, they will have to be very lucky indeed to gain
access to the protected area. This might be inconvenient for authentic users,
however is usually a price worth paying.
Finally, make sure you
track IP addresses of both those users who successfully login and those who
don't. If you spot repeated attempts from a single IP address to access the
site, you may consider blocking access from that IP address altogether.
Database Users
One excellent way to
make sure that even if you have a problem with someone accessing your database
who shouldn't be able to, you can limit the damage they can cause. Modern
databases like MySQL and SQL Server allow you to control what a user can and
cannot do. You can give users (or not) permission to create data, edit, delete,
and more using these permissions. Usually, I try and ensure that I only allow
users to add and edit data.
If a site requires an
item be deleted, I will usually set the front end of the site to only appear to
delete the item. For example, you could have a numeric field called
"item_deleted", and set it to 1 when an item is deleted. You can then
use that to prevent users seeing these items. You can then purge these later if
required, yourself, while not giving your users "delete" permissions
for the database. If a user cannot delete or drop tables, neither can someone
who finds out the user login to the database (though obviously they can still
do damage).
Powerful Commands
PHP contains a variety
of commands with access to the operating system of the server, and that can
interact with other programs. Unless you need access to these specific
commands, it is highly recommended that you disable them entirely.
For example, the eval() function allows you to
treat a string as PHP code and execute it. This can be a useful tool on
occasion. However, if using the eval() function on any input
from the user, the user could cause all sorts of problems. You could be,
without careful input validation, giving the user free reign to execute
whatever commands he or she wants.
There are ways to get
around this. Not using eval() is a good start.
However, the php.ini file gives you a way to completely disable certain
functions in PHP - "disable_functions". This directive of the php.ini
file takes a comma-separated list of function names, and will completely
disable these in PHP. Commonly disabled functions include ini_set(), exec(), fopen(), popen(), passthru(), readfile(), file(),shell_exec() and system().
It may be (it usually
is) worth enabling safe_mode on your server. This instructs PHP to limit the
use of functions and operators that can be used to cause problems. If it is
possible to enable safe_mode and still have your scripts function, it is
usually best to do so.
Finally, Be Completely
and Utterly Paranoid
Much as I hate to
bring this point up again, it still holds true (and always will). Most of the
above problems can be avoided through careful input validation. Some become
obvious points to address when you assume everyone is out to destroy your site.
If you are prepared for the worst, you should be able to deal with anything.
No comments:
Post a Comment