Connect with Facebook Lost Password?


Go Back   CodeCall Programming Forum > Software Development > Tutorials > Security Tutorials

Security Tutorials Tutorials on how to protect your software against crackers.

Reply
 
LinkBack Thread Tools Search this Thread Display Modes
  #1 (permalink)  
Old 09-23-2007, 10:08 PM
John's Avatar   
Co-Administrator
 
Join Date: Jul 2006
Age: 20
Posts: 5,306
Blog Entries: 24
Rep Power: 20
John is just really niceJohn is just really niceJohn is just really niceJohn is just really niceJohn is just really nice
Send a message via AIM to John Send a message via MSN to John
Default PHP: SQL Injections

In today's world of rapidly expanding technology, most of us have become completely dependent on the internet. For many of us, it serves as our main method of banking, holiday shopping, people use it to find their soul mate, and some of us even use it as our main method of social interaction [thats not me]. So it is a no-brainer to understand why keeping our data secure is important. The scary part is, all of these actions involve the storage of your personal information on a remote server and the security of your private information, is completely dependent on the security of the environment in which you interact. Which is why webmasters need to take into account the strength of their database queries. One of the most common security flaws is the ability of code to be injected into a database query and perform malicious actions. This is known as a SQL Injection.

What is an SQL injection?

Before I fully divulge what an SQL injection is, lets setup a simple scenario. Bob is a freelance programmer heired by the Extreme Banking Association to development an online banking system, which stores the credit card information of its users. Bob creates a mysql database similar to the following structure:

SQL Code:
  1. CREATE TABLE `users` (
  2.   `id` int(11) NOT NULL AUTO_INCREMENT,
  3.   `username` varchar(200) NOT NULL,
  4.   `password` varchar(200) NOT NULL,
  5.   `creditcard` varchar(200) NOT NULL,
  6.   PRIMARY KEY  (`id`)
  7. );

He then imports some data into the database:
SQL Code:
  1. INSERT INTO `users` (`id`, `username`, `password`, `creditcard`) VALUES
  2. (1, 'Sidewinder', 'monkey', '0123456789987654'),
  3. (2, 'John', 'password!', '3123456769384659'),
  4. (3, 'Bob', '1234', '7133116752374638');

At this point, the manager of the project should see Bob's incompetence and lack of skill. What kind of idiot would store passwords in plain text? Anyway, Bob continues with this code and creates a basic login system, and it has a structure similar to the following:

PHP Code:
  1. <?php
  2. if(empty($_GET['username'])) {
  3.  
  4. echo "<form method='GET' action='login.php'>"
  5.     ."Username: <input type='text' name='username' /><br />"
  6.     ."Password: <input type='text' name='password' /><br />"
  7.     ."<input type='submit' value='Login' />"
  8.     ."</form>";
  9. }
  10.  
  11.    $username = stripslashes($_GET['username']);
  12.    $password = stripslashes($_GET['password']);
  13. }
  14.  
  15. $link = mysql_connect($dbhost, $dbuser, $dbpass)
  16.     or die('Could not connect: ' . mysql_error());
  17.  
  18. mysql_select_db($mysqldb, $link)
  19.     or die('Could not select database.');
  20.  
  21. $query = mysql_query("SELECT * FROM `users` "
  22.         . "WHERE `username` = '$username' "
  23.         . "AND `password` = '$password'")
  24.     or die('Could not select database.');
  25.  
  26. $row = mysql_fetch_assoc($query);
  27.  
  28. if(mysql_num_rows($query) >= 1) {
  29.     echo "Hello {$row['username']}!<br />";
  30.     echo "Your credit card number is: {$row['creditcard']}";
  31. }
  32. ?>

At this point, it does everything the manager of the project wants, enter your username/password and the users credit card number is displayed. So, Bob withdraws his money from the escrow account and goes on his merry way. One day an ub3r l33t h4x0r comes along, and attempts an SQL injection. He sees that the form uses a GET method request, and attempts to alter the data that is sent to the server. He enters the following line into his address bar:

Quote:
login.php?username=anything&password=anything'%20O R%201='1
And this is displayed on this screen:
Quote:
Hello Sidewinder!
Your creditcard number is: 0123456789987654
And the hacker has successfully gained access to the database, and thus, all information in that database. As you can see from the URL, the provided username is anything and the password is anything'%20OR%201='1. The magic is in the password. In essence it translates to:
Quote:
anything' or 1='1
And when that is injected into the query, it looks like this:
PHP Code:
  1. $query = mysql_query("SELECT * FROM `users` "
  2.         . "WHERE `username` = 'anything "
  3.         . "AND `password` = 'anything' OR 1='1'");
The query is evaluated as logically true. Although the username/password conjunction evaluates to false, the disjunctive 1=1 clause is evaluated to be true, hence the query returns the results. In the example above, the returns the results of the data in the first row. However, the injection can be altered to perform other actions. In most cases, the injection is rather complicated due tosome security measures taken by the programmer. This leads us to the next section of this article.

How do I prevent SQL injections?

The answer is simple – never trust user input. That is the single most important concept in developing a secure application. The concept is simple, but taking action is a little more difficult.

The easiest way to prevent SQL injections, is to escape data. The PHP developers implemented a feature(?) called Magic Quotes, which escapes quotes, NULL characters, backslashes, among other characters. This was designed as a security feature to help prevent SQL injections, however has caused both me, and many developers headaches, which is probably why as of PHP 6.0, Magic Quotes will be depreciated. As a result, it is a poor idea to rely on Magic Quotes as a solution to your SQL injections. In fact, even with Magic Quotes enabled, SQL injections are still possible. The code I am going to provide you is probably not the most secure code, as I am far from an expert on security, but I have found it as a decent solution for many of my problems regarding SQL injections.

First thing we want to do is remove all of the damage done by Magic Quotes:
PHP Code:
  1. // Is magic quotes on?
  2.  
  3. if (get_magic_quotes_gpc()) { // Yes? Strip the added slashes
  4.     $_REQUEST = array_map('stripslashes', $_REQUEST);
  5.     $_GET = array_map('stripslashes', $_GET);
  6.     $_POST = array_map('stripslashes', $_POST);
  7.     $_COOKIE = array_map('stripslashes', $_COOKIE);
  8. }

Next, take advantage of the mysql_real_escape_string() function in our query:

PHP Code:
  1. $query = mysql_query("SELECT * FROM `users` "
  2.         . "WHERE `username` = '"
  3.         . mysql_real_escape_string($username) . "' "
  4.         . "AND `password` = '"
  5.         . mysql_real_escape_string($password) . "'");
Now I am going to change the form method from GET to POST which, although provides no explicit security, implicitly it does.
PHP Code:
  1. if(empty($_GET['username'])) {
  2. echo "<form method='POST' action='login.php'>"
  3.     ."Username: <input type='text' name='username' /><br />"
  4.     ."Password: <input type='text' name='password' /><br />"
  5.     ."<input type='submit' value='Login' />"
  6.     ."</form>";
  7. }
  8. $username = $_POST['username'];
  9. $password = $_POST['password'];

Finally, strengthen the session validation, and the final script will look like this:
PHP Code:
  1. <?php
  2. // Is magic quotes on?
  3. if (get_magic_quotes_gpc()) { // Yes? Strip the added slashes
  4.     $_REQUEST = array_map('stripslashes', $_REQUEST);
  5.     $_GET = array_map('stripslashes', $_GET);
  6.     $_POST = array_map('stripslashes', $_POST);
  7.     $_COOKIE = array_map('stripslashes', $_COOKIE);
  8. }
  9.  
  10. if(empty($_POST['username'])) {
  11.     echo "<form method='POST' action='login.php'>"
  12.     ."Username: <input type='text' name='username' /><br />"
  13.     ."Password: <input type='text' name='password' /><br />"
  14.     ."<input type='submit' value='Login' />"
  15.     ."</form>";
  16. }
  17.  
  18. $username = $_POST['username'];
  19. $password = $_POST['password'];
  20.  
  21. $link = @mysql_connect($dbhost, $dbuname, $dbpassword)
  22.     or die('Could not connect: ' . mysql_error());
  23.  
  24. mysql_select_db($mysqldb, $link)
  25.     or die('Could not select database.');
  26.  
  27. $query = mysql_query("SELECT * FROM `users` "
  28.         . "WHERE `username` = '"
  29.         . mysql_real_escape_string($username) . "' "
  30.         . "AND `password` = '"
  31.         . mysql_real_escape_string($password) . "'");
  32.  
  33. $row = mysql_fetch_assoc($query);
  34.  
  35. if((mysql_num_rows($query) >= 1) && ($password == $row['password'])) {
  36.     echo "Hello {$row['username']}!<br />";
  37.     echo "Your credit card number is: {$row['creditcard']}";
  38. }
  39. ?>

As far as SQL injections go, you should be pretty safe!

Last edited by John; 12-15-2007 at 10:39 PM..
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #2 (permalink)  
Old 09-23-2007, 11:55 PM
Retired
 
Join Date: Apr 2007
Posts: 2,975
Blog Entries: 3
Rep Power: 33
v0id is a glorious beacon of lightv0id is a glorious beacon of lightv0id is a glorious beacon of lightv0id is a glorious beacon of lightv0id is a glorious beacon of lightv0id is a glorious beacon of light
Default

Pretty good article.
There's a little bug in your SQL injection though. I don't think the SQL injection you come up with would work correctly.
Code:
' OR 1 = '1
As you see, you're comparing an integer (1) with a character/string ('1') I'm not sure if SQL cares about it or not, but I'd prefer to compare a character/string and a character/string.
Code:
' OR '1' = '1
It was only a little note, beside that; good work!

I actually made a blogpost on this topic a while ago, if anyone should be interested.

Last edited by v0id; 09-24-2007 at 07:38 AM..
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #3 (permalink)  
Old 09-24-2007, 03:14 AM
TcM's Avatar   
TcM TcM is online now
Code Warrior
 
Join Date: Aug 2006
Posts: 9,840
Blog Entries: 6
Rep Power: 82
TcM is a name known to allTcM is a name known to allTcM is a name known to allTcM is a name known to allTcM is a name known to allTcM is a name known to all
Default

Thanks very much! When I saw this I thought that my website was hacked because of it, but I tested it and at least it is not vulnerable to this threat. So I can eliminate an SQL Injection. Thanks and great tutorial/article.
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #4 (permalink)  
Old 09-24-2007, 07:42 AM
Jordan's Avatar   
Administrator
 
Join Date: Nov 2005
Location: Hendersonville, NC
Posts: 18,370
Blog Entries: 90
Rep Power: 20
Jordan is a glorious beacon of lightJordan is a glorious beacon of lightJordan is a glorious beacon of lightJordan is a glorious beacon of lightJordan is a glorious beacon of light
Send a message via ICQ to Jordan Send a message via AIM to Jordan Send a message via MSN to Jordan Send a message via Yahoo to Jordan
Default

Excellent article! Thank you for the Tutorial.
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #5 (permalink)  
Old 09-24-2007, 10:10 AM
John's Avatar   
Co-Administrator
 
Join Date: Jul 2006
Age: 20
Posts: 5,306
Blog Entries: 24
Rep Power: 20
John is just really niceJohn is just really niceJohn is just really niceJohn is just really niceJohn is just really nice
Send a message via AIM to John Send a message via MSN to John
Default

Quote:
Originally Posted by v0id View Post
Pretty good article.
There's a little bug in your SQL injection though. I don't think the SQL injection you come up with would work correctly.
Code:
' OR 1 = '1
As you see, you're comparing an integer (1) with a character/string ('1') I'm not sure if SQL cares about it or not, but I'd prefer to compare a character/string and a character/string.
Code:
' OR '1' = '1
It was only a little note, beside that; good work!

I actually made a blogpost on this topic a while ago, if anyone should be interested.
I wasn't very concerned with a correct injection as the isn't a tutorial on how to do injections - rather how to prevent them. I'm rather happy the injection won't work.
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #6 (permalink)  
Old 09-24-2007, 01:10 PM
Retired
 
Join Date: Apr 2007
Posts: 2,975
Blog Entries: 3
Rep Power: 33
v0id is a glorious beacon of lightv0id is a glorious beacon of lightv0id is a glorious beacon of lightv0id is a glorious beacon of lightv0id is a glorious beacon of lightv0id is a glorious beacon of light
Default

I just thought it would be nicer with correct information, but you're right, it's a cliché. Anyways, like I said before, good work.
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #7 (permalink)  
Old 09-24-2007, 07:26 PM
John's Avatar   
Co-Administrator
 
Join Date: Jul 2006
Age: 20
Posts: 5,306
Blog Entries: 24
Rep Power: 20
John is just really niceJohn is just really niceJohn is just really niceJohn is just really niceJohn is just really nice
Send a message via AIM to John Send a message via MSN to John
Default

Quote:
Originally Posted by v0id View Post
I just thought it would be nicer with correct information, but you're right, it's a cliché. Anyways, like I said before, good work.
Touché. I was actually going to create a login system and host it here as a demo, but I didn't have time. Although I would still like to do so in the future.
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #8 (permalink)  
Old 12-15-2007, 10:39 PM
John's Avatar   
Co-Administrator
 
Join Date: Jul 2006
Age: 20
Posts: 5,306
Blog Entries: 24
Rep Power: 20
John is just really niceJohn is just really niceJohn is just really niceJohn is just really niceJohn is just really nice
Send a message via AIM to John Send a message via MSN to John
Wink

updated
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #9 (permalink)  
Old 06-22-2008, 08:29 PM
Whitey's Avatar   
Programming Professional
 
Join Date: Feb 2008
Location: Loveland, Colorado
Posts: 236
Rep Power: 9
Whitey has a spectacular aura aboutWhitey has a spectacular aura aboutWhitey has a spectacular aura about
Send a message via AIM to Whitey Send a message via MSN to Whitey Send a message via Skype™ to Whitey
Default Re: PHP: SQL Injections

uhmm question...

why do you use
Code:
#
if (get_magic_quotes_gpc()) { // Yes? Strip the added slashes
    $_REQUEST = array_map('stripslashes', $_REQUEST);
    $_GET = array_map('stripslashes', $_GET);
    $_POST = array_map('stripslashes', $_POST);
    $_COOKIE = array_map('stripslashes', $_COOKIE);
}
rather then using something like this?
Code:
#
if(get_magic_quotes_gpc()) {
   $username = stripslashes($_GET['username']);
   $password = stripslashes($_GET['password']);
}

because i usually use that 1 then do the mysql_real_escape thing
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #10 (permalink)  
Old 06-22-2008, 09:09 PM
John's Avatar   
Co-Administrator
 
Join Date: Jul 2006
Age: 20
Posts: 5,306
Blog Entries: 24
Rep Power: 20
John is just really niceJohn is just really niceJohn is just really niceJohn is just really niceJohn is just really nice
Send a message via AIM to John Send a message via MSN to John
Default Re: PHP: SQL Injections

Excellent question. Your method achieves the exact outcome as mine; however, you should note that $_GET is an (associative) array. Using array_map and passing it the callback function stripslashes(), you can strip the slashes of every element in the $_GET array by simply calling that single function. Whereas you call stripslashes() on every element individually, using my method, it is not necessary (as array_map will do it automatically).

Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
Reply



Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On


Similar Threads
Thread Thread Starter Forum Replies Last Post
PHP 4 end of life announcement Jordan News 4 08-30-2007 09:55 AM
Question: free webhosting with PHP, SMTP, and at least one SQL database skilletsteve Hosting and Registrars 5 12-08-2006 04:36 AM


All times are GMT -5. The time now is 03:33 PM.

Freelance Jobs

XML/XSL: Need code for Book with Chapers using XML
Create an XML file for a book of your creation, and a basic CSS file that will format it to display ...
Earn: $40.00


C++/C: Simple firework cue sequencer
What I require is a rework of a simple cue sequencer. I have a piece of hardware (an Arduino boar...
Earn: $50.00


HTML/XHTML: Menu Rework - ASCIIBin
I'm placing this in the HTML/XHTML section of the Freelance site but you are not limited to HTML. Wh...
Earn: $20.00



CodeCall Goal

Goal #1: 1,000 Blogs
Goal #2: 1,000 Wiki Pages
Goal #3: 300,000 Posts
Goal #4: 20,000 Threads
Done: 30%, 23%, 56%, 75%

Ads