Writing Secure PHP scripts

One of the most overlooked things in Web Development - especially in PHP - is making sure that your scripts are secure. Here’s some details of some basic mistakes people make when programming websites in PHP, and how these little mistakes can be used by any malicious user to gain access to the website. This is by no means a full article, but it covers the basic errors, and how to remove them from your code.

Ultimately, a ‘hacker’ wants to gain access to a website for a variety of reasons. Maybe to steal users details, bank account information etc, or even just to simply deface it. Either way, as programmers, we have to be aware of the methods they use so that we can write code that is secure.
This article will cover 4 basic areas:

  • Register Globals
  • XSS ( Cross Site Scripting ) attacks
  • Session Id Stealing
  • SQL Injections

Register Globals
Register globals is perhaps the oldest vulnerability in PHP. and also the simplest for hackers to exploit.

What is Register Globals?
Register globals is a simple feature added to PHP to make data input handling easier. Say we have a form input page, with a textbox called ‘username’ box:

1
2
echo $_POST['username'];     // conventional method
echo $username;

Instead of having to get the value from the $_POST array, register globals automatically creates a variable $username, and assigns it the value from the form. This works for both vales sent by GET and POST methods.

Example
Suppose we have a members only webpage, and in order to check if the user is a valid member, we check for a cookie on the users system, that would be set when the person logged in.

1
2
3
4
5
6
7
8
9
10
11
12
13
if($_COOKIE['secret_site'])
{
$auth = 1;
}
if ($auth != 1)
{
die("You are not allowed in");
}
else
{
echo "Welcome to the secret admin area";
}
?>

This is a very basic script, but it illustrates the problem of register globals well. At a a glance, this script looks fin, and is we try to run it,
we can see that we are not allowed access.

However, now lets try loading the script, but adding a GET value to the query string.

register_globals.php?auth=1

We still dont have the cookie, but register globals means a hacker doesnt need it. Sending a variable called auth with a value 1 in the GET values, a variable $auth is craeted with the value of 1. So, even though we do not have the cookie:

1
2
3
4
5
6
7
8
if ($auth != 1)
{
die("You are not allowed in");        // register_globals sets $auth to 1 so we dont enter this if statement
}
else
{
echo "Welcome to the secret admin area";    // we enter here, without the cookie!
}

Solution

The solution to this is very simple, make sure register_globals is turned off.
This can be completed by editing the php config file (php.ini) or can also be set using code using the ini_set()function.

Note that latter versions of php have register_globals turned off as default so the sample script may not work. Remember to check your hosts configuration to protect your site.

XSS Attacks

Cross site scripting is probably the most common exploit used by hackers at the moment, so we have to be very aware of it when we are writing code. It all stems from the problem that programmers forget to validate and sanitize data that users have input.

example

Suppose we have a blog like site we have made in php, with a part at the bottom where users can leave comments.

Also suppose that the users details are stores in a cookie when they login, as many websites do dor security.

I have simulated this here by just issuing a cookie for a user “steve” with a password “secret_pass”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 
setcookie("mysite", implode(array('username'=>'steve','password'=>'secret_pass'), '|||'));
 
if(!isset($_POST['message'] ))
{
 
echo '
<h1>Please Enter your Comment</h1>
<form style="color: #0000ff;" action="&lt;span">"./xss_hack.php"</form>method=<span style="color: #0000ff;">"post"</span>&gt;
<span style="color: #ff8000;"><textarea style="color: #0000ff;" name="&lt;span">"message"&lt;/span&gt; &gt;&lt;/span&gt;&lt;span style="color: #ff8000;" mce_style="color: #ff8000;"&gt;</textarea></span>
 
<input style="color: #0000ff;" type="&lt;span" />"submit" value=<span style="color: #0000ff;">"Submit"</span>&gt;
 
';
 
}
 
else {
 
echo '
<h1>This was Your Comment</h1>
'.stripslashes($_POST['message']).'
 
';
 
}
 
?>

Now this looks very innoscent, and its not obvious atall what is wrong with it.
But now try this, enter this as your message:

1
2
3
<script type="text/javascript"><!--
 alert(document.cookie);
// --></script>

What happens here is that we have entered some html as the message. This html is a script tag, containing soem javascript. The javascript pops up a message box with the contents of the cookie. Again, you might think, so what? but what if instead of being javascript code to popup a box, it instead sent this cookie information to the hacker?
Any user that is logged in and loads that comments page will have their username and password stolen without even knowing. This is the power of XSS.

XSS With Sessions
So the first thought is, ok, i’ll use sessions to store the users details, instead of a cookie. Bad idea.
Sessions have a unique session ID that is stored either in a cookie, or in the GET variables query string, i.e.

xss_hack.php?session_id=g4n48474g4746gdjf89495

Either way, this is easy pickings for the hacker.
If the session id is stored in a cookie, the hacker just uses the same injected html as before, and creates a cookie on his computer with teh same value, and he/she has access to your user account. Or if the session is stored in the string, the hacker simply modifies the javascript to take this, and then they simply point their browser to the same page, with your session id added on.

Solutions
Ultimately, the solution is pretty simple. Whenever inputing data from users to your website/database,
sanatize it! Make sure you strip out all html characters.

SQL Injections
The final method we will look at is SQL injections. These are a very cute and clevor way of a hacker gaining access to your website. They are called SQL injections as inadvertantly, extra data is added into the SQL query.

Example
Lets suppose we have a standard login page, with a username and password box.
We also have a database, containing the users username/password.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
 
$db_handle = mysql_connect('localhost', 'root', 'your_password');
mysql_select_db('security_tutorial');
 
if(!isset($_POST['username']) || !isset($_POST['password']))
{
 
echo '
<h1>Please Enter your username and password to proceed</h1>
<form style="color: #0000ff;" action="&lt;span">"./sql_injection.php" method=<span style="color: #0000ff;">"post"</span>&gt;
Username:
 
<input style="color: #0000ff;" type="&lt;span" />"text" name=<span style="color: #0000ff;">"username"</span> /&gt;
 
Password:
 
<input style="color: #0000ff;" type="&lt;span" />"password" name=<span style="color: #0000ff;">"password"</span> /&gt;
 
<input style="color: #0000ff;" type="&lt;span" />"submit" value=<span style="color: #0000ff;">"Submit"</span>&gt;
 
</form>';
}
 
else {
 
//entered details
$entered_username = stripslashes($_POST['username']);
$entered_password = stripslashes($_POST['password']);
 
echo "SELECT * FROM users WHERE users_username='$entered_username' AND users_password='$entered_password'";
 
//get users details
$resultset = mysql_query("SELECT * FROM users WHERE users_username='$entered_username' AND users_password='$entered_password'");
 
//if record found, grant access
if(mysql_num_rows($resultset) != 1)
{
die('Invalid Username/Password');
}
 
//else proceed
 
$sUsername = mysql_result($resultset,0,'users_username');
 
//selcome user, and exit
die("Welcome to the members only area ".$sUsername);
 
}
 
?>

Try running this on your local computer.

Try logging in, with some random details.
If i tell you the username is ’steve’, try some random passwords.
It seems to work properly, but there is a problem.
If we look at the line where the database is queried:

1
2
//get users details
$resultset = mysql_query("SELECT * FROM users WHERE users_username='$entered_username' AND users_password='$entered_password'");

The query is ran as shown, with the entered username and password spliced in.

Lets look at the query closer:

1
SELECT * FROM users WHERE users_username='$entered_username' AND users_password='$entered_password'"

Now, we know the username is steveso we can enter that. But Suppose we enter this for the password:

‘OR ‘1′ = ‘1

See what this makes the query into:

1
SELECT * FROM users WHERE users_username='steve' AND users_password=''OR '1' = '1'"

This is the SQL injection, and it is very clevor.
The WHERE clause of the query says that

1
WHERE users_username='steve' AND users_password=''OR '1' = '1'"

Now, the password will never be null, but it reads OR 1=1, which will always be true.

Therefore, a hacker will be granted access to your website without a password.

Solutions

Again, same as before, make sure that you filter out all characters and html other than the things you want. In this case it is especially importnat to remove the single and double quote characters (’ and “).

Thats it for now, hope you found this article interesting. Ill post another soon.

Share and Enjoy:

  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google
  • Blogsvine
  • E-mail this story to a friend!
  • LinkedIn
  • Live
  • NewsVine
  • Print this article!
  • Reddit
  • Slashdot
  • Spurl
  • StumbleUpon
  • Technorati
  • YahooMyWeb

Tags: , , , ,

Leave a Reply