How to prevent an SQL injection in PHP?

How to prevent SQL injection in PHP?

  • If user input is inserted into an SQL query directly, the application becomes vulnerable to http://stackoverflow.com/questions/332365/xkcd-sql-injection-please-explain/332367#332367/1228, like in the following example: $unsafe_variable = $_POST['user_input']; mysql_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?

  • Answer:

    Use prepared statements and parameterized queries. These are SQL statements that are sent to and parsed by the database server separately from any parameters. If you use http://php.net/manual/en/book.pdo.php you can work with prepared statements like this: $preparedStatement = $db->prepare('SELECT * FROM employees WHERE name = :name'); $preparedStatement->execute(array(':name' => $name)); $rows = $preparedStatement->fetchAll(); where $db is a PDO object. The http://www.php.net/manual/en/book.mysqli.php also provides parameterized queries. 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: $preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)'); $preparedStatement->execute(array(':column' => $unsafeValue));

Andrew G. Johnson at Stack Overflow Visit the source

Was this solution helpful to you?

Other answers

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 better practice, but escaping characters in your variable will require fewer changes. We'll do the simpler string escaping one first. //connect $safe_variable = mysql_real_escape_string($_POST["user-input"]); mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')"); //disconnect See also, the details of the http://php.net/mysql_real_escape_string function. To use the paramterised query, you need to use http://php.net/mysqli rather than the http://php.net/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 http://php.net/mysqli.prepare. Also, as others have suggested, you may find it useful/easier to step up a layer of abstraction with something like http://php.net/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 http://stackoverflow.com/users/978756/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.

Matt Sheppard

I'd recommend using http://ca3.php.net/manual/en/book.pdo.php (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.

Kibbee

Always use parameterized queries. Always. There is no way to get the bad guy's code into your SQL if you don't put it into the source code of the query. It may take some learning, but it is the only way to go.

Andy Lester

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();

Imran

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.

Rob

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.

Your Common Sense

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 its a very good stepping stone. i left out obvious items such as checking the variable's existance, format (numbers, letters, etc)

Tanerax

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 here http://www.tizag.com/mysqlTutorial/mysql-php-sql-injection.php

rahularyansharma

Just Added Q & A:

Find solution

For every problem there is a solution! Proved by Solucija.

  • Got an issue and looking for advice?

  • Ask Solucija to search every corner of the Web for help.

  • Get workable solutions and helpful tips in a moment.

Just ask Solucija about an issue you face and immediately get a list of ready solutions, answers and tips from other Internet users. We always provide the most suitable and complete answer to your question at the top, along with a few good alternatives below.