Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

If user input is inserted into an SQL query directly, the application becomes vulnerable to SQL injection, like in the following example:

$unsafe_variable = $_POST['user_input'];

mysqli_query("INSERT INTO table (column) VALUES ('" . $unsafe_variable . "')");

That's because the user can input something like value'); DROP TABLE table;--, making the query:

INSERT INTO table (column) VALUES('value'); DROP TABLE table;--')

What should one do to prevent this?

share|improve this question
99  
Note that that particular example will not work, because the mysql_ lib does not allow executing 2 queries in one statement. As long as you keep the manipulation in a single statement the injection will work. – Johan Aug 19 '12 at 14:08
10  
It also shouldn't work if you have given proper permissions to the user and assuming that the average sql user should not be granted permissions to drop tables it shouldn't work. – VBAssassin Nov 29 '12 at 12:15
126  
Please, don't use mysql_* functions in new code. They are no longer maintained and are officially deprecated. See the red box? Learn about prepared statements instead, and use PDO or MySQLi - this article will help you decide which. If you choose PDO, here is a good tutorial. – Neal Dec 20 '12 at 17:47
3  
Here's an article I wrote on how to convert your mysql_ scripts to mysqli_, and get started using prepared statements: stackoverflow.com/a/15055993/1270996 – Nicholas Pickering Feb 24 at 22:09
5  
SQL Injection ---> php.net/manual/en/security.database.sql-injection.php – Dasun Mar 6 at 10:46
show 3 more comments

30 Answers

up vote 1783 down vote accepted
+50

Use prepared statements and parameterized queries. These are SQL statements that are sent to and parsed by the database server separately from any parameters. This way it is impossible for an attacker to inject malicious SQL.

You basically have two options to achieve this:

  1. Using PDO:

    $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    
    $stmt->execute(array(':name' => $name));
    
    foreach ($stmt as $row) {
        // do something with $row
    }
    
  2. Using mysqli:

    $stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
    $stmt->bind_param('s', $name);
    
    $stmt->execute();
    
    $result = $stmt->get_result();
    while ($row = $result->fetch_assoc()) {
        // do something with $row
    }
    

PDO

Note that when using PDO to access a MySQL database real prepared statements are not used by default. To fix this you have to disable the emulation of prepared statements. An example of creating a connection using PDO is:

$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

In the above example the error mode isn't strictly necessary, but it is advised to add it. This way the script will not stop with a Fatal Error when something goes wrong. And gives the developer the chance to catch any error(s) which are thrown as PDOExceptions.

What is mandatory however is the first setAttribute() line, which tells PDO to disable emulated prepared statements and use real prepared statements. This makes sure the statement and the values aren't parsed by PHP before sending it to the MySQL server (giving a possible attacker no chance to inject malicious SQL).

Although you can set the charset in the options of the constructor it's important to note that 'older' versions of PHP (< 5.3.6) silently ignored the charset parameter in the DSN.

Explanation

What happens is that the SQL statement you pass to prepare is parsed and compiled by the database server. By specifying parameters (either a ? or a named parameter like :name in the example above) you tell the database engine where you want to filter on. Then when you call execute the prepared statement is combined with the parameter values you specify.

The important thing here is that the parameter values are combined with the compiled statement, not a SQL string. SQL injection works by tricking the script into including malicious strings when it creates SQL to send to the database. So by sending the actual SQL separately from the parameters you limit the risk of ending up with something you didn't intend. Any parameters you send when using a prepared statement will just be treated as strings (although the database engine may do some optimization so parameters may end up as numbers too, of course). In the example above, if the $name variable contains 'Sarah'; DELETE * FROM employees the result would simply be a search for the string "'Sarah'; DELETE * FROM employees", and you will not end up with an empty table.

Another benefit with using prepared statements is that if you execute the same statement many times in the same session it will only be parsed and compiled once, giving you some speed gains.

Oh, and since you asked about how to do it for an insert, here's an example (using PDO):

$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');

$preparedStatement->execute(array(':column' => $unsafeValue));
share|improve this answer
20  
the link in this document appears to be out of date, I believe the following link will work: php.net/manual/en/book.pdo.php – Dave Nov 1 '09 at 19:19
9  
This make sense, However, PDO is an extension right? meaning it needs to be installed? Is there a way I can check to see if it is installed? Also I am using a shared hosting, so if it is not installed and my hosting provider cannot/will not install it, is there an alternative to using a PDO? Thank You!! – JD Isaacks Jan 5 '10 at 21:31
90  
php.net/manual/en/pdo.installation.php PDO is bundled by default since PHP 5.1. Not all drivers for all databases may be installed, but if your host supports MySQL and PHP later than 5.1 it would be very surprising if it didn't have the MySQL PDO driver installed. Create a page with <?php phpinfo(); ?> and view it in a browser, look for PDO and you will see info on which drivers are installed. – Theo Jan 6 '10 at 12:29
25  
The protection comes from using bound parameters, not from using prepared statement (it is just that people tend to switch to using prepared statements at the same time as bound parameters, so the two ideas get conflated). – Quentin Oct 3 '11 at 10:53
11  
With the postgres extension: $result = pg_query_params( $dbh, 'SELECT * FROM users WHERE email = $1', array($email) ); – Quentin Nov 22 '11 at 15:48
show 14 more comments

You've got two options - escaping the special characters in your unsafe_variable, or using a parameterized query. Both would protect you from SQL injection. The parameterized query is considered the better practice, but escaping characters in your variable will require fewer changes.

We'll do the simpler string escaping one first.

//Connect

$unsafe_variable = $_POST["user-input"]
$safe_variable = mysql_real_escape_string($unsafe_variable);

mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

//Disconnect

See also, the details of the mysql_real_escape_string function.

To use the parameterized query, you need to use MySQLi rather than the MySQL functions. To rewrite your example, we would need something like the following.

<?php
    $mysqli = new mysqli("server", "username", "password", "database_name");

    // TODO - Check that connection was successful.

    $unsafe_variable = $_POST["user-input"];

    $stmt = $mysqli->prepare("INSERT INTO table (column) VALUES (?)");

    // TODO check that $stmt creation succeeded

    // "s" means the database expects a string
    $stmt->bind_param("s", $unsafe_variable);

    $stmt->execute();

    $stmt->close();

    $mysqli->close();
?>

The key function you'll want to read up on there would be mysqli::prepare.

Also, as others have suggested, you may find it useful/easier to step up a layer of abstraction with something like PDO.

Please note that the case you asked about is a fairly simple one, and that more complex cases may require more complex approaches. In particular:

  • If you want to alter the structure of the SQL based on user input, parameterised queries are not going to help, and the escaping required is not covered by mysql_real_escape_string. In this kind of case you would be better off passing the user's input through a whitelist to ensure only 'safe' values are allowed through.
  • If you use integers from user input in a condition and take the mysql_real_escape_string approach, you will suffer from the problem described by Polynomial in the comments below. This case is trickier because integers would not be surrounded by quotes, so you could deal with by validating that the user input contains only digits.
  • There are likely other cases I'm not aware of. You might find http://webappsec.org/projects/articles/091007.txt a useful resource on some of the more subtle problems you can encounter.
share|improve this answer
12  
I like this much better than the accepted answer! But is mysql_real_escape_string really as safe as parameterization? – Cawas Apr 29 '11 at 14:57
25  
Something is very wrong in PHP land if mysql_real_escape_string doesn't appropriately escape all special characters. That said, it's easier to look at code using parameterization and know that it's correct than code using escaping functions. – Matt Sheppard May 2 '11 at 0:33
82  
-1 because concatenation-style query building is always a bad idea. The mysql_real_escape_string function is not a catch-all. It only escapes special characters, so SELECT * FROM users WHERE score = $var is still vulnerable to $var = "1 OR 1 = 1". – Polynomial Dec 5 '11 at 12:25
5  
I can't think of an example where they are, but haven't looked into it deeply. Cedric below pointed out webappsec.org/projects/articles/091007.txt which may provide some useful info. – Matt Sheppard Dec 7 '11 at 2:20
16  
-1 escaping parameters is a form of blacklisting: any failure results in a vulnerability. As @polynomial stated, this is a bad idea. Ideally, mysql_real_escape_string and its ilk would be removed from PHP (given their track record, I won't hold my breath), so as to prevent that false sense of security. Parameterize or be pnwed. – BryanH Jul 3 '12 at 22:20
show 26 more comments

I'd recommend using PDO (PHP Data Objects) to run parameterized SQL queries. Not only does this protect against SQL injection, it also speeds up queries. And by using PDO rather than mysql_, mysqli_, and pgsql_ functions, you make your app a little more abstracted from the database, in the rare occurence that you have to switch database providers.

share|improve this answer
8  
In MySQL, this can actually make performance worse since prepared statements aren't cached and also, the query cache won't be used for prepared statements. This blog post is old, but I believe still up-to-date with respect to caching info mysqlperformanceblog.com/2006/08/02/mysql-prepared-statements – Michael Mior Jul 8 '11 at 17:27
3  
This can be (somewhat) solved via $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); to emulate prepared statements client(PHP)-side. – Michael Mior Jul 8 '11 at 17:29
30  
I'm pretty sure that prepared statements are cached since MySQL 5.1.17. – Justin ᚅᚔᚈᚄᚒᚔ Jul 8 '11 at 17:45

Every answer here covers only part of the problem.
In fact, there are four different query parts which we can add to it dynamically:

  • a string
  • a number
  • an identifier
  • a syntax keyword.

and prepared statements covers only 2 of them

But sometimes we have to make our query even more dynamic, adding operators or identifiers as well.
So, we will need different protection techniques.

In general, such a protection approach is based on whitelisting. In this case every dynamic parameter should be hardcoded in your script and chosen from that set.
For example, to do dynamic ordering:

$orders  = array("name","price","qty"); //field names
$key     = array_search($_GET['sort'],$orders)); // see if we have such a name
$orderby = $orders[$key]; //if not, first one will be set automatically. smart enuf :)
$query   = "SELECT * FROM `table` ORDER BY $orderby"; //value is safe

However, there is another way to secure identifiers - escaping. As long as you have an identifier quoted, you can escape backticks inside by doubling them.

As a further step we can borrow a truly brilliant idea of using some placeholder (a proxy to represent the actual value in the query) from the prepared statements and invent a placeholder of another type - an identifier placeholder.

So, to make long story short: it's a placeholder, not prepared statement can be considered as a silver bullet.

So, a general recommendation may be phrased as
As long as you are adding dynamic parts to the query using placeholders (and these placeholders properly processed of course), you can be sure that your query is safe.

Still there is an issue with SQL syntax keywords (such as AND, DESC and such) but whitelisting seems the only approach in this case.

share|improve this answer
3  
What we could really do with is a clean, simple library to implement this - it's certainly not in PDO, and I really don't want the wasteful overhead of prepared queries just to get half-baked parameter binding. – Synchro Dec 5 '12 at 10:59
4  
@Synchro Finally I am done with it, and I have to say that I'm proud of my work, as I've got it easy, fast and lightweight. You can find the link in my profile. – Your Common Sense Feb 15 at 18:11
Awesome man. Found it on your github. Thinking about implementing it very soon. – Alex Jun 25 at 1:46

Use PDO and prepared queries.

($conn is a PDO object)

$stmt = $conn->prepare("INSERT INTO tbl VALUES(:id, :name)");
$stmt->bindValue(':id', $id);
$stmt->bindValue(':name', $name);
$stmt->execute();
share|improve this answer

Whatever you do end up using, make sure that you check your input hasn't already been mangled by magic_quotes or some other well-meaning rubbish, and if necessary, run it through stripslashes or whatever to sanitise it.

share|improve this answer
24  
...or just turn magic quotes off... – Kzqai Apr 15 '11 at 14:32
5  
Indeed; running with magic_quotes switched on just encourages poor practice. However, sometimes you can't always control the environment to that level - either you don't have access to manage the server, or your application has to coexist with applications that (shudder) depend on such configuration. For these reasons, it's good to write portable applications - though obviously the effort is wasted if you do control the deployment environment, e.g. because it's an in-house application, or only going to be used in your specific environment. – Rob Apr 24 '11 at 17:04
5  
As of PHP 5.4, the abomination known as 'magic quotes' has been killed dead. And good riddance to bad rubbish. – BryanH Jan 16 at 22:45

Injection Prevention - mysql_real_escape_string()

PHP has a specially-made function to prevent these attacks. All you need to do is use the mouthful of a function mysql_real_escape_string.

What mysql_real_escape_string does is take a string that is going to be used in a MySQL query and return the same string with all SQL Injection attempts safely escaped. Basically, it will replace those troublesome quotes(') a user might enter with a MySQL-safe substitute, an escaped quote \'.

NOTE: you must be connected to the database to use this function!

// connect to MySQL

$name_bad = "' OR 1'"; 

$name_bad = mysql_real_escape_string($name_bad);

$query_bad = "SELECT * FROM customers WHERE username = '$name_bad'";
echo "Escaped Bad Injection: <br />" . $query_bad . "<br />";


$name_evil = "'; DELETE FROM customers WHERE 1 or username = '"; 

$name_evil = mysql_real_escape_string($name_evil);

$query_evil = "SELECT * FROM customers WHERE username = '$name_evil'";
echo "Escaped Evil Injection: <br />" . $query_evil;

you can find more detail about SQL Injection Prevention.

share|improve this answer
7  
This is the best you can do with legacy mysql extension. For new code, you're advised to switch to mysqli or PDO. – Álvaro G. Vicario Feb 26 at 12:42
1  
I am not agree with this 'a specially-made function to prevent these attacks'. I think that mysql_real_escape_string purpose is in allow to build correct SQL query for every input data-string. Prevention sql-injection is the side-effect of this function. – sectus Jul 9 at 5:01

You could do something basic like this:

$safe_variable = mysql_real_escape_string($_POST["user-input"]);
mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

This won't solve every problem, but it's a very good stepping stone. I left out obvious items such as checking the variable's existence, format (numbers, letters, etc.).

share|improve this answer
9  
I have tried your example and it's work fine for me.Could you clear "this won't solve every problem" – Chinook Apr 22 '12 at 20:31
1  
If you don't quote the string, it's still injectable. Take $q = "SELECT col FROM tbl WHERE x = $safe_var"; for example. Setting $safe_var to 1 UNION SELECT password FROM users works in this case because of the lack of quotes. It's also possible to inject strings into the query using CONCAT and CHR. – Polynomial Apr 16 at 18:06
@Polynomial Completely right, but I'd see this merely as wrong usage. As long as you use it correctly, it will definitely work. – glglgl Jul 10 at 7:30
@glglgl That's fine, if you're willing to accept the risk of someone forgetting a quote somewhere in your entire application. Bad development practices keep me in a job ;) – Polynomial Jul 10 at 9:05

Parameterized query AND input validation is the way to go. There is many scenarios under which SQL injection may occur, even though mysql_real_escape_string() has been used.

Those examples are vulnerable to SQL injection :

$offset = isset($_GET['o']) ? $_GET['o'] : 0;
$offset = mysql_real_escape_string($offset);
RunQuery("SELECT userid, username FROM sql_injection_test LIMIT $offset, 10");

or

$order = isset($_GET['o']) ? $_GET['o'] : 'userid';
$order = mysql_real_escape_string($order);
RunQuery("SELECT userid, username FROM sql_injection_test ORDER BY `$order`");

In both case you can't use ' to protect the encapsulation.

source : The Unexpected SQL Injection (When Escaping Is Not Enough)

share|improve this answer

As you can see, people suggest you to use prepared statements at the most. Its not wrong, but when your query is executed just once per process, there would be a slightly performance penalty. I was facing this issue but I think i solved it very very sophisticated way - the way hackers use to avoid using quotes. I used this in conjuction with emulated prepared statements. I use it to prevent all possible sql injection attacks.

My approach:

  • if you expect input to be integer make sure its really integer. In variable-type language like is php is this very important. You can use for example this very simple but powerful solution: sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);
  • if you expect anything else from integer hex it. If you hex it, you will perfectly escape all input. In C/C++ there's a function called mysql_hex_string(), in php use bin2hex(). Dont worry about that the escaped string will have 2x size of its original length because even if you use mysql_real_escape_string or bin2hex, php have to allocate same capacity ((2*input_length)+1). This hex method is often used when you transfer binary data but I see no reason why not use it to all data to prevent sql injection attacks. Note that you have to prepend data with 0x or use mysql function UNHEX.

So for example query:

SELECT password FROM users WHERE name = 'root'

Will become:

SELECT password FROM users WHERE name = 0x726f6f74

Hex is the perfect escape. No way to inject.

Note that this hex method is often used as a sql injection attack where integers are just like strings and escaped just with mysql_real_escape_string, then you can avoid use of quotes.

For example if you just do something like this:

"SELECT title FROM article WHERE id = " . mysql_real_escape_string($_GET["id"])

attack can inject you very easily. Consider the following injected code returned from your script:

SELECT ... WHERE id = -1 union all select table_name from information_schema.tables

and

SELECT ... WHERE id = -1 union all select table_name from information_schema.tables where table_name = 0x61727469636c65

But if would the coder of injectable site hex it, no injection would be possible:

SELECT ... WHERE id = 0x2d312075...3635

share|improve this answer
3  
This is such a simple and effective answer. I wish I could upvote it twice. – dadinck Mar 6 at 19:30
1  
For a numeric (decimal) argument, don't add '0x' to the beginning and use unhex('$coded'). – dadinck Mar 6 at 19:31
2  
@Zaffy got to admit, the two proposed solutions are elegant beyond any solution I've seen to date. You sir and your answer deserve much more recognition! – Khez Mar 11 at 12:57
1  
@YourCommonSense You are wrong! Number 42 are two bytes 4 and 2 so the result will be 0x3432. Also be aware of using it like that because if id doesnt contain anything you will get id = 0x and end up with an error. – Zaffy Mar 19 at 8:49
2  
A note to readers who still can't get the point: This answer contradicts with itself. It cannot solve the very problem query that posted as a bad example. This answer is wrong and deceiving. The approach is even worse than regular manual formatting, not to mention prepared statements. – Your Common Sense Jul 1 at 14:18
show 17 more comments

The right way to prevent SQL injection is by using parameterized queries. This means defining the SQL code that is to be executed with placeholders for parameter values, programmatically adding the parameter values, then executing the query. Doing this allows the server to create an execution plan for the query, which prevents any "injected" SQL from being executed. An example will help in explaining this. Let’s use the same script, but I’ll define the SQL query with parameter placeholders:

$sql = "SELECT * FROM Tbl WHERE Username = ? and Password = ?";

Now, I’ll define an array that holds the parameter values:

$params = array($_POST['Username'], $_POST['Password']);

When I execute the query, I pass the $params array as an argument:

$stmt = sqlsrv_query($conn, $sql, $params);
share|improve this answer
1  
But never store the user's password in plain text in the database. Store a hash function, using a salt derived from the rest of the record. – Eric Jablow May 2 at 3:24
4  
@EricJablow: This is just example that show how to prevent SQL injection , not to how to show password !!! – Nirav Ranpara May 2 at 5:01

I favor stored procedures (mySQL has sp support since 5.0) from a security point of view - the advantages are -

  1. Most databases (including mySQL) enable user access to be restricted to executing stored procedures. The fine grained security access control is useful to prevent escalation of privileges attacks. This prevents compromised applications from being able to run SQL directly against the database.
  2. They abstract the raw SQL query from the application so less information of the db structure is available to the application. This makes it harder or people to understand the underlying structure of the database and design suitable attacks.
  3. They accept only parameters so the advantages of parameterized queries are there. of course - IMO you still need to sanitize your input - especially if you are using dynamic SQL inside the stored procedure.

The disadvantages are -

  1. They (stored procedures) are tough to maintain and tend to multiply very quickly. This makes managing them an issue.
  2. They are not very suitable for dynamic queries - if they are built to accept dynamic code as parameters then a lot of the advantages are negated.
share|improve this answer
2  
I have noticed a lot of down-votes to this answer but no comments or any reasons as to why. I would appreciate the courtesy of letting me know why you think this answer deserves a down-vote so i have an opportunity to respond. – Nikhil Mar 2 '11 at 9:16
13  
I haven't downvoted, but stored procedures are generally frowned upon, because you put business logic, which belongs into your PHP scripts, into the database, making maintainance a nightmare. – NikiC Apr 10 '11 at 8:58
2  
@Nikhil: Stored procedures themselves do little to protect against SQL injection. Unless you use parametrized queries to run the stored procedure, an attacker can still inject malicious SQL into a query. The only real benefit that you've cited is that it hides the database structure, but that doesn't mean that attacks are very much more difficult. The rest of the benefits you claim are just so much nonsense. – greyfade Apr 20 '11 at 17:07
2  
@nikic - Yes, using stored procedures may encourage people to put business logic in them when it is not appropriate. But IMO that is a code smell which should be caught in your code review. – Nikhil Apr 25 '11 at 5:54
1  
Sanitizing the input is of of the points I made in my answer - point three. In this point I mention that sps are not suitable for dynamic queries and this is one of the reasons why. Assuming for a moment one is able to compromise a stored procedure using SQL injection (becuase the sql injection happened through a string parameter and was then used in the query). In this case the limited access privileges of the stored procedure (assuming you have set it up correctly) will limit the damage to the parts of the database the sp has access to. – Nikhil Apr 25 '11 at 6:39
show 12 more comments

Type cast if possible your parameters. But it's only working on simple types like int, bool and float.

$unsafe_variable = $_POST['user_id'];

$safe_variable = (int)$unsafe_variable ;

mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
share|improve this answer
5  
This is often used way which is not too good. I am been penetration tester and usually used this to pass arguments like 999999999999999999 which go smoothly through cast and later cause huge error message when passed to 4 bytes integer storage. – Tõnu Samuel Jun 25 '12 at 3:53
1  
using intval() should prevent this behavior, right? – devOp Jul 20 '12 at 7:38
2  
Not exactly. Manual says "The maximum value depends on the system. 32 bit systems have a maximum signed integer range of -2147483648 to 2147483647. So for example on such a system, intval('1000000000000') will return 2147483647. The maximum signed integer value for 64 bit systems is 9223372036854775807.". I think it is not clever idea to have PHP app which uses 4 byte ints in MySQL to rely on this "feature". It breaks on 64 bit systems. I think web apps should check for values to remain in some range to be sure. – Tõnu Samuel Jul 23 '12 at 9:14
1  
Well the easy solution is substr( $unsafe_variable, 0, 10 ) (keep only first 10 characters of post variable) – bobobobo Apr 12 at 23:22

There are many ways of preventing SQL injections and other SQL hacks. You can easily find it on the Internet (Google Search). Of course PDO is one of the good solution. But I would like to suggest you some good links prevention from SQL Injection.

What is SQL injection and how to prevent

PHP manual for SQL injection

Microsoft explanation of SQL injection and prevention in PHP

and some other like Preventing SQL injection with MySQL and PHP

Now, why you do you need to prevent your query from SQL injection?

I would like to let you know: Why do we try for preventing SQL injection with a short example below:

Query for login authentication match:

$query="select * from users where email='".$_POST['email']."' and password='".$_POST['password']."' ";

Now, if someone (a hacker) puts

$_POST['email']= [email protected]' OR '1=1

and password anything....

The query will be parsed in the system only upto:

$query="select * from users where email='[email protected]' OR '1=1';

The other part will be discarded. So, what will happen? A non-authorized user (hacker) will be able to login as admin without having his password. Now, he can do anything what admin/email person can do. See, it's very dangerous if SQL injection is not prevented.

share|improve this answer
1  
Just for pedantry; given "SELECT * FROM users WHERE email = '" . $_POST['email'] . "'"; the malicious user would likely foo@bar' or '1' = '1 thus creating an altogether new expression of '1' = '1' – Bracketworks May 31 at 18:13

In my opinion, the best way to generally prevent SQL injection in your PHP app (or any web app, for that matter) is to think about your application's architecture. If the only way to protect against SQL injection is to remember to use a special method or function that does The Right Thing every time you talk to the database, you are doing it wrong. That way, it's just a matter of time until you forget to correctly format your query at some point in your code.

Adopting the MVC pattern and a framework like CakePHP or CodeIgniter is probably the right way to go: Common tasks like creating secure database queries have been solved and centrally implemented in such frameworks. They help you to organize your web app in a sensible way and make you think more about loading and saving objects than about securely constructing single SQL queries.

share|improve this answer

For those unsure of how to use PDO (coming from the mysql_ functions), I made a very, very simple PDO wrapper that is a single file. It exists to show how easy it is to do all the common things applications need done. Works with PostgreSQL, MySQL, and SQLite.

Basically, read it while you read the manual to see how to put the PDO functions to use in real life to make it simple to store and retrieve values in the format you want.

I want a single column

$count = DB::column('SELECT COUNT(*) FROM `user`);

I want an array(key => value) results (i.e. for making a selectbox)

$pairs = DB::pairs('SELECT `id`, `username` FROM `user`);

I want a single row result

$user = DB::row('SELECT * FROM `user` WHERE `id` = ?', array($user_id));

I want an array of results

$banned_users = DB::fetch('SELECT * FROM `user` WHERE `banned` = ?', array(TRUE));
share|improve this answer
using static methods as a namespace isn't generally a good solution – yes123 Apr 7 at 22:07
@yes123, there is no namespacing in my example. It is simply a static class. – Xeoncross Apr 8 at 2:22
you are using it as a namespace and it's not so good. Anyway I gave you a +1. For other information look at stackoverflow.com/questions/4690478/functions-vs-static-methods – yes123 Apr 9 at 23:09
I think you're confused about the difference between namespaces and proper inheritance design. If it was named something like Micro_DB (part of a Micro library) then I would need to update it to \Micro\DB in order to be PHP 5.3+ compliant and maintain the correct form of class segregation. However, it is merely a single class without any form of project/library designation. Perhaps you see all static classes as a namespace of sorts, however the class does maintain state and shared variables even though it's not initialized like an object, which means it's not just a namespaced wrapper. – Xeoncross Apr 10 at 15:20
1  
Oh yes, static methods are generally always a sign of bad design. I was just saying that it had nothing to do with namespacing. However, I believe my tiny library is an exception since the point was simply an illustrative library which 1) would never be extended and 2) would never have unit tests (do to the target audience and size of the codebase). My real database/ORM libraries are all namespace classes which are to be used as real and extendable objects as they should be. – Xeoncross Apr 10 at 15:47
show 1 more comment

Using this PHP function mysql_escape_string() you can get a good prevention in a fast way.

For example:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."'

mysql_escape_string — Escapes a string for use in a mysql_query

For more prevention you can add at the end ...

wHERE 1=1   or  LIMIT 1

Finally you get:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."' LIMIT 1
share|improve this answer
WHERE 1=1 or LIMIT 1 does you no good if they inject ');DROP TABLE users;-- . It's beyond me why people just don't use parameterized ("prepared") statements for this and be done with it. – Craig Ringer Apr 28 at 11:10
This answer is just a duplicate of other wrong questions. – Your Common Sense Jul 1 at 11:22

If you want to take advantage of cache engines like redis or memcache maybe DALMP could be a choice, it uses pure mysqli, check this: http://www.dalmp.com/PExecute

Also you can 'prepare' your arguments before preparing your query so that you can build dynamic queries and at the end have a full prepared statements query. http://www.dalmp.com/Prepare

share|improve this answer

I think if someone want use PHP and MySQL:

  1. Think about lerning PDO
  2. Think about mysqli
  3. Use native php functions like: strip_tags, mysql_real_escape_string or if variable numeric just (int) $Foo
share|improve this answer

I use three different ways to prevent my web application from being vulnerable to SQL injection.

  1. Use of mysql_real_escape_string(), which is a pre-defined function in PHP, and this code add backslashes to the following characters: \x00, \n, \r, \, ', " and \x1a. Pass the input values as parameters to minimize the chance of SQL injection.

  2. Use of MySQLi.

  3. The most advanced way is to use PDOs.

I hope this will help you.

share|improve this answer

Regarding to many useful answers, I hope to add some values to this thread. SQL injection is type of attack that can be done through user inputs (Inputs that filled by user and then used inside queries), The SQL injection patterns are correct query syntax while we can call it: bad queries for bad reasons, we assume that there might be bad person that try to get secret information (by passing access control) that affect the three principles of security (Confidentiality, Integrity, Availability).

Now, our point is to prevent security threats such as SQL injection attacks, the question asking (How to prevent SQL injection attack using PHP), be more realistic, data filtering or clearing input data is the case when using user-input data inside such query, using PHP or any other programming language is not the case, or as recommended by more people to use modern technology such as prepared statement or any other tools that currently supporting SQL injection prevention, consider that these tools not available anymore? how you secure your application?

My approach against SQL injection is: clearing user-input data before sending it to database (before using it inside any query).

Data filtering for (Converting unsafe data to safe data) Consider that PDO and mysqli not available, how can you secure your application? do you force me to use them? what about other languages other than PHP? I prefer to provide general ideas as it can be used for wider border not just for specific language.

  1. SQL user (limiting user privilege) : most common SQL operations are (SELECT, UPDATE, INSERT), then, why giving UPDATE privilege to a user that not require it? for example: login, and search pages are only using SELECT, then, why using db users in these pages with high privileges? RULE: do not create one db user for all privileges, for all SQL operations, you can create your scheme like (deluser, selectuser, updateuser) as usernames for easy usage.

  2. Data filtering: before building any query user input should be validated and filtered, for programmers, its important to define some properties for each user-input variables: data type, data pattern, and data length. a field that is a number between (x and y) must be exactly validated using exact rule, for a field that is a string (text): pattern is the case, for example: username must contain only some characters lets say [a-zA-Z0-9_-.] the length vary between (x and n) where x and n (integers, x <=n ). Rule: creating exact filters and validation rules are best practice for me.

  3. Use other tools: Here, I will also agree with you that prepared statement (parametrized query) and Stored procedures, the disadvantages here is these ways requires advanced skills which are not exist in most users, the basic idea here is to distinguish between SQL query and the data that being used inside, both approach can be used even with unsafe data, because the user-input data here not add anything to the original query such as (any or x=x). for more information please read OWASP SQL Injection Prevention Cheat Sheet.

Now, if you are an advanced user, start using these defense as you like, but, for beginners, if they can't quickly implement stored procedure and prepared statement, its better to filter input data as much they can.

Finally, lets consider that user sends this text below instead of entering his username:

[1] UNION SELECT IF(SUBSTRING(Password,1,1)='2',BENCHMARK(100000,SHA1(1)),0) User,Password FROM mysql.user WHERE User = 'root'

This input can be checked early without any prepared statement and stored procedures, but to be on safe side, using them starts after user-data filtering and validation.

Last point is detecting unexpected behavior which requires more effort and complexity, its not recommended for normal web applications. Unexpected behavior in above user input is: SELECT, UNION, IF, SUBSTRING, BENCHMARK, SHA, root once these words detected, you can avoid the input.

References:

  1. SQL Injection Cheat Sheet
  2. SQL Injection
  3. Information security
  4. Security Principles
  5. Data validation
share|improve this answer
5  
This is absolutely pointless answer. 1. SELECT-based injection is a disaster alone. So, #1 is quite useless. 2. Data filtering won't help for the most of real life usage. Imagine one were used on Stack Overflow - this answer just were unable to happen, as it's full of "evil" words like SELECT, UNION and even complete "malicious" BENCHMARK query. Thus, #2 is inapplicable too. 3. "Use other tools" is not a protection measure at all. So, #3 is as pointless as other two. – Your Common Sense Mar 19 at 6:08
2  
you can improve it, or flag it to be removed :) thanks for your comment... – Akam Mar 19 at 6:12
1  
This answer is bad! YourCommonSense outlined most of the reasons, but I'm throwing a downvote in for the thought that beginners can't use a prepared statement. Ridiculous. I would argue that it's probably easier to use prepared statements than concatenate garbage into a query in the first place. You should delete your answer. – Brad Jun 14 at 6:19
1  
Brad: Thanks for your idea, bad or good should not be used for scientific judgment, I see that you are not academic person, and not professional. You should use correct, incorrect, valid, invalid, and explain why, I will not consider any word from @Your Common Sense, if you want to explain your own reason, you are welcome. – Akam Jun 18 at 21:52

The simple alternative to this problem could be solved by granting appropriate permissions in the database itself. For example: if you are using mysql database. then enter into the database through terminal or the ui provided and just follow this command:

 GRANT SELECT, INSERT, DELETE ON database TO username@'localhost' IDENTIFIED BY 'password';

This will restrict the user to only get confined with the specified query's only. Remove the delete permission and so the data would never get deleted from the query fired from the php page. The second thing to do is to flush the privileges so that the mysql refreshes the permissions and updates.

FLUSH PRIVILEGES; 

more information about flush.

To see the current privileges for the user fire the following query.

    select * from mysql.user where User='username';

Learn more about GRANT.

share|improve this answer
3  
It does not solve injection problem, not even slightest. Even only SELECT rights are harmful. This is not a solution but rather a placebo. One could use it for whatever else purpose but isql injection protection. – Your Common Sense Feb 18 at 6:36
4  
Operating with minimum privileges is a useful damage control strategy to make life harder for the attacker and limit the damage they can do, but it's a bit like saying "my house is on fire, but it's fine, I keep the kerosene locked safely in the garage." Your house is still on fire. – Craig Ringer Apr 28 at 11:06

A few guidelines for escaping special characters in SQL statements.

Don't use MySQL, this extension is deprecated, use MySQLi or PDO.

MySQLi

For manually escaping special characters in string you can use mysqli_real_escape_string function. The function will not work properly unless the correct character set is set with mysqli_set_charset.

Example:

$mysqli = new mysqli( 'host', 'user', 'password', 'database' );
$mysqli->set_charset( 'charset');

$string = $mysqli->real_escape_string( $string );
$mysqli->query( "INSERT INTO table (column) VALUES ('$string')" );

For automatic escaping of values with prepared statements, use mysqli_prepare, and mysqli_stmt_bind_param where types for the corresponding bind variables must be provided for an appropriate conversion:

Example:

$stmt = $mysqli->prepare( "INSERT INTO table ( column1, column2 ) VALUES (?,?)" );

$stmt->bind_param( "is", $integer, $string );

$stmt->execute();

No matter if you use prepared statements or mysqli_real_escape_string, you always have to know the type of input data you're working with.

So if you use prepared statement, you must specify the types of the variables for mysqli_stmt_bind_param function.

And use of mysqli_real_escape_string is for, as the name says, escaping special characters in a string, so it will not make integers safe. The purpose of this function is to prevent breaking the strings in sql statements, and the damage to the database that it could cause. mysqli_real_escape_string is a useful function when used properly, especially when combined with sprintf.

Example:

$string = "x' OR name LIKE '%John%";
$integer = '5 OR id != 0';

$query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string( $string ), $integer );

echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 5

$integer = '99999999999999999999';
$query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string( $string ), $integer );

echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 2147483647 
share|improve this answer

How protect index.php by sql-injection using .htaccess was not really a duplicate! Some have reported it as a duplicate question to this question. So, I'll post my answer here, an admin can even move my answer from here to a so called duplicate question:

However, if the attackers is trying to hack with the form via PHP $_GET variable or with the URL's query string, you can able to catch them if they're not secure.

RewriteCond %{QUERY_STRING} ([0-9]+)=([0-9]+)
RewriteRule ^(.*) ^/track.php

Because 1=1, 2=2, 1=2, 2=1, 1+1=2, etc... are the common questions to SQL database of an attacker. Maybe also it's used by many hacking applications. But you must be careful, that you must not rewrite a safe queries from your site. The code above is giving you a tip, to rewrite or redirect (depends on you) that hacking-specific dynamic query string into a page that will store the attacker's I.P. address, or EVEN THEIR COOKIES, history, browser, or any other sensitive informations, and try to hack them back for security purpose.

share|improve this answer
4  
I am afraid that admin would rather just delete this answer. This approach is just impractical. it's impossible to catch ALL the patterns using mod_rewrite rules. Not to mention that some of them can be perfectly legit. And what about POST requests? – Your Common Sense Apr 4 at 10:27
2  
that's the point. As PHP the language will do the job anyway, all this mod_rewrite unreliable magic become useless. – Your Common Sense Apr 4 at 11:35
1  
Hacking back attacker is a not acceptable approach, especially on this site - and impractical, as @YourCommonSense says. I recommend to modify your answer to something "try to ban them out from your site". Because that is more acceptable purpose. – Gabor Garami Apr 21 at 15:11
1  
-1, this is probably the worst answer I've seen on Stack Overflow. – Brad Jun 14 at 6:15
1  
Bypassing this is very easy. – Zaffy Jun 16 at 22:25
show 3 more comments

A simple way would be to use a PHP framework like CodeIgniter or Laravel which have in-built features like filtering and active-record, so that you dont have to worry about these nuances.

share|improve this answer
1  
It fits for basic CRUD operations only. – Your Common Sense Jun 25 at 19:36
Im using Active Record with CI too. No problems so far (3 years) – Steve Muster Jun 25 at 23:45
@SteveMuster well, that's good for you. Unfortunately, I have to use full SQL, not such a limited subset offered by CI. Say, sometimes I have run not just insert-and-forget but INSERT IGNORE or INSERT DELAYED. Not just selects but index hinting and such. And so on. – Your Common Sense yesterday

Although not a perfect solution, you can get a big deal of protection for free, merely by using the ESAPI for PHP, from OWASP

Caution: they say their current release is not suitable for production use. I'd take that with a grain of salt, though.

share|improve this answer
One don't need no such junk to protect from injection. Just format your queries properly and you are safe. As simple as that. – Your Common Sense yesterday
1  
Why the downvote? Is the use of ESAPI a valid answer to the question, or not? @YourCommonSense: that you don't like ESAPI I can understand. That there are other, perhaps simpler (in your opinion, at least) courses of action, I'm sure as well. But that doesn't mean ESAPI is useless, does it? – Bruno Unna 19 hours ago
It is. It makes code bloated for no reason. Also, I smell some sort of blacklisting approach it its filtering (i didn't dig too deep to be sure), which makes whole approach unusable – Your Common Sense 9 hours ago

The best protection you can get is is you know what you are doing in the first place.

Many small and large and very large websites have been hacked by use of injection due to programmers overconfidence in their skills.

To be able to write good secure code in the first place you need to understand know how injections work.

PHP is naively stupid at handling variable types and thus the main reason why most injections are done on PHP based sites.

The majority of programmers who use MySQL have never written a user function or a trigger, most never know what they are exactly and think they are very good programmer, some have heard about stored procedures but are not really sure how they work. Once you have a good clue on the true power of a database engine and a programming language and have studied all the fortes and weaknesses then you can start programming secure code.

One of my most important moment in my programmers life was after an interview when I realized I am not as good as I thought I was. That's when I really started to learn programming.

Just my two noob cents.

share|improve this answer

if for any reasons /old habits :) ?/ using classic mysql_... functions then maybe I'd recomend either safing all variables and not forget close them in quotes, or using a multisafeizer and then use the "safeized" vars:

this example solves whole REQUEST at once, maybe u can just specify which vars to do, or just POST, or just GET, your choice.. using dynamic variable name assigning (doubledollar) u can use the variable name to set a new with similar name like $$k = $v if wished

remember on some server you'd have to use stripslashes before safeizing as PHP may add them to all quotes and simmilar characters...

<?php

safevar($x)
{ // even add quotes here before and after var if u wish, before returning value
  if (get_magic_quotes_gpc()) { $x=stripslashes($x);}
  return mysql_real_escape_string($x);
}

foreach($_REQUEST as $k=>$v)
{ $safe[$k] = safevar($v); } 

// .. and then ... $safe[colA] instead of $_GET['colA'], ...
mysql_query("insert into table (colA,colB) values ".
            "('{$safe[colA]}','{$safe[colB]}','{$safe[colC]}')");
share|improve this answer
3  
Please, why not to read other answers before writing your own? All these wrong practices already discussed zillion times. your "safevar" function doesn't make anything safe but only spoil the input data. While "double dollar" is actually a breach in security. – Your Common Sense Jul 10 at 6:49
@YourCommonSense totally agree .. why are people even adding new answers to this ! – ManseUK Jul 10 at 8:44
ok about doubledollar - used wrong yes it can harm a lot, I agree, but please can u explain or provide a link to why u think the above safevar (stripslashes + mysql_real_escape..) "does nothing but spoil data" ??? if discussed zillion times sure u find a link to why that real escape string is not ok, yeah ? – Peminator Jul 12 at 10:40

use the php function named mysqli_real_escape_string for every string user inputs. and you are safe.

Simple but most effective

share|improve this answer
2  
Unfortunately, it's not simple nor effective. – Your Common Sense Jul 10 at 6:28
Not simple to change content everywhere in your application, and you must ALWAYS do this or you are leaving a hole. Secondly the implementation is via the database, so your string is passed to the database to be filtered and back to your application on each call, causing additional load on the database. – TJChambers Jul 12 at 19:13
$query = sprintf("select column_name from table_name where column_name = %d", mysql_real_escape_string(the columns value in where));

mysql_query($query)

in the place of integer you use %d in string %s

share|improve this answer

protected by AVD Jun 6 '12 at 9:59

This question is protected to prevent "thanks!", "me too!", or spam answers by new users. To answer it, you must have earned at least 10 reputation on this site.

Not the answer you're looking for? Browse other questions tagged or ask your own question.