Skip to main content

Best way to stop SQL Injection in PHP


So specifically in a mysql database, I understand that this is vulnerable to SQL injection . Take the following code and tell me what to do.




// connect to the mysql database

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

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

// disconnect from the mysql database


Source: Tips4allCCNA FINAL EXAM

Comments

  1. 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 PHP Data Objects 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 mysqli class 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));

    ReplyDelete
  2. 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 mysql_real_escape_string function.

    To use the paramterised 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.

    ReplyDelete
  3. 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.

    ReplyDelete
  4. 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.

    ReplyDelete
  5. 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();

    ReplyDelete
  6. 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.

    ReplyDelete
  7. Parametrized sql (check your sql provider) plus htmlentities

    ReplyDelete
  8. 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.

    ReplyDelete
  9. 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)

    ReplyDelete
  10. 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)

    ReplyDelete
  11. Anyone suggesting anything besides prepared statements or parametrized queries deserves a downvote:

    Parametrized queries in mySQL:

    http://us.php.net/manual/en/mysqli.prepare.php

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


    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.
    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.
    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 -


    They (stored procedures) are tough to maintain and tend to multiply very quickly. This makes managing them an issue.
    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.

    ReplyDelete
  13. interger

    $num = (int)$num;


    character

    $str = htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); // escape 5 chars ' " < > &
    if (!get_magic_quotes_gpc()) {
    $str = addslashes($str); // escape 4 chars ' '' \ \0
    }
    if ($type == 'sql') {
    $arr = array('_' => "\_", '%' => "\%");
    $str = strtr($str, $arr);
    }

    ReplyDelete
  14. I use a combination of sprintf and mysql_real_escape_string functions...

    For example, if I want to select a row using its id value:

    $query = "SELECT `name` FROM `names` WHERE `id` = %u";
    $query = sprintf($query, $id_val);


    that way I make sure only positive integers get through the query...

    And when I want to insert a string into the query I use "%s" instead of "%u" (you can find more about it on the PHP documentation I linked above) with a combination of mysql_real_escape_string.

    Although I'm sure it's not 100% safe, there is always a way to bypass checks... One thing that comes to mind is maybe using hex values?

    ReplyDelete
  15. I would say something like this in addition to mysql_real_escape_string():

    <?php
    // Quote variable to make safe
    function quote_smart($value)
    {
    // Stripslashes
    if (get_magic_quotes_gpc()) {
    $value = stripslashes($value);
    }
    // Quote if not integer
    if (!is_numeric($value)) {
    $value = "'" . mysql_real_escape_string($value) . "'";
    }
    return $value;
    }
    ?>

    ReplyDelete

Post a Comment

Popular posts from this blog

[韓日関係] 首相含む大幅な内閣改造の可能性…早ければ来月10日ごろ=韓国

div not scrolling properly with slimScroll plugin

I am using the slimScroll plugin for jQuery by Piotr Rochala Which is a great plugin for nice scrollbars on most browsers but I am stuck because I am using it for a chat box and whenever the user appends new text to the boxit does scroll using the .scrollTop() method however the plugin's scrollbar doesnt scroll with it and when the user wants to look though the chat history it will start scrolling from near the top. I have made a quick demo of my situation http://jsfiddle.net/DY9CT/2/ Does anyone know how to solve this problem?

Why does this javascript based printing cause Safari to refresh the page?

The page I am working on has a javascript function executed to print parts of the page. For some reason, printing in Safari, causes the window to somehow update. I say somehow, because it does not really refresh as in reload the page, but rather it starts the "rendering" of the page from start, i.e. scroll to top, flash animations start from 0, and so forth. The effect is reproduced by this fiddle: http://jsfiddle.net/fYmnB/ Clicking the print button and finishing or cancelling a print in Safari causes the screen to "go white" for a sec, which in my real website manifests itself as something "like" a reload. While running print button with, let's say, Firefox, just opens and closes the print dialogue without affecting the fiddle page in any way. Is there something with my way of calling the browsers print method that causes this, or how can it be explained - and preferably, avoided? P.S.: On my real site the same occurs with Chrome. In the ex