Most new web developers have heard of SQL injection attacks, but not very many know that it is fairly easy to prevent an attacker from gaining access to your data by filtering out the vulnerabilities using MySQL extensions found in PHP.
An SQL injection attack occurs when a hacker or cracker (a malicious hacker) attempts to dump the data in a database table in a database-driven web site. In an unprotected and vulnerable site, this is pretty easy to do.
In order for an SQL injection attack to work, the site must use an unprotected SQL query that utilizes data submitted by a user to lookup something in a database table. The data could be from a search box, a login form or any type of query used to look up data using data input by user. It also means that querystring data used to query a database can create vulnerabilities.
For example:
An very simple unprotected query might look like this:
SELECT * FROM items WHERE itemID = '$itemID'
Normally, you would expect a user to submit a username and password, which would be used to query the database table to see if the username and password exists. But what if someone used the following instead of a password?
‘ OR ‘1’ = ‘1
That would make the query used to look for the password look like this:
SELECT * FROM items WHERE itemID = '' OR '1' = '1'
This would always return a True response and could literally display the entire table as the result for the query. This is a pretty scary thought if you are trying to keep your data secure. The problem with SQL injection is that a hacker does not have to know anything about your database or table structure.
What if an error or some other issue caused your table structure to be exposed? Hackers are very good at forcing errors to occur that expose information that allows them to penetrate a site deeper. What if the following was entered in the password field?
‘; drop table users;
There is a method for filtering the data that is used on the right side of the WHERE clause to look up a row in a database. The trick is to escape any characters that may be in the user input portion of the query that could lead to a successful attack.
Use the following function to add backslashes to suspect characters and filter any data that is input by a user.
function cleanQuery($string) { if(get_magic_quotes_gpc()) // prevents duplicate backslashes { $string = stripslashes($string); } if (phpversion() >= '4.3.0') { $string = mysql_real_escape_string($string); } else { $string = mysql_escape_string($string); } return $string; } // if you are using form data, use the function like this: if (isset($_POST['itemID'])) $itemID = cleanQuery($_POST['itemID']); // you can also filter the data as part of your query: SELECT * FROM items WHERE itemID = '". cleanQuery($itemID)."' "
The first part looks to see if magic quotes is turned on. if so, it may have already added backslash escapes though a POST or GET method used to pass the data. If backslashes were added, they need to be removed prior to running it through the rest of the function.
The next part checks the PHP version. The built-in function that we want to use is called mysql_real_escape_string. This MySQL function only exists in PHP version 4.3.0 or newer. If you are using an older version of PHP, another MySQL function is used called mysql_escape_string.
mysql_escape_string is not as effective as the newer mysql_real_escape_string. The newer version escapes the string according to the current character set. The character set is ignored by mysql_escape_string, which can leave some vulnerabilities ope for sophisticated hackers. If you find that you are using an older version of PHP and you are trying to protect sensitive data, you really should upgrade to a current version of either PHP 4 or PHP 5.
So what does mysql_real_escape_string do?
This PHP library function prepends backslashes to the following characters: \n, \r, \, \x00, \x1a, ‘ and “. The important part is that the single and double quotes are escaped, because these are the characters most likely to open up vulnerabilities.
For those who do not know what an escape is, it is a character that is pre-pended to another character. When a character is escaped, it is ignored by the database. In other words, it makes that character ineffective in a query. In the case of PHP, an escaped character is treated differently by the PHP parser. The standard escape character used by PHP and MySQL is the backslash.
In the case of the SQL query example used above, after running it through the routine, it now looks like this, which breaks the query :
SELECT * FROM items WHERE itemID = '\' OR \'1\' = \'1'
This method should stop the bulk of the SQL injection attacks, but crackers and hackers are very creative and are always finding new methods to break into systems. There are additional steps that can be taken to filter out certain words, such as drop, grant, union, etc., but using this method will strip these words from searches performed by you users. However, if you want to add another level of security and do not have an issue with certain words being deleted from queries, you can add the following just before if (phpversion() >= ‘4.3.0’).
$badWords = array("/delete/i", "/update/i","/union/i","/insert/i","/drop/i","/http/i","/--/i"); $string = preg_replace($badWords, "", $string);
This additional step should prevent a malicious attacker from damaging a database if they found a way to slip through. Just remember that is you take this additional step and you have a site where someone might search for a “plumbing union” or a “drop cloth”, those queries would not work as intended. If you are wondering what the trailing ‘i’ is following each word in the array, it is required to make the preg_replace replacements case insensitive. This wasn’t needed with eregi_replace, but that function was deprecated (discontinued) in PHP 5.3.
Another important step that needs to be taken with any database is controlling user privileges. When setting up a MySQL user, you should never assign any more privileges than they actually need to accomplish the tasks that you allow on your site. Privaleges are easily assigned and managed thought phpMyAdmin, which is found in the the control panel (cPanel, Plesk, etc.) for most hosting companies.