Question

My PHP application has a query that takes a md5 hash as an input, from a user via GET method, then it applies $mysqli->real_escape_string() to it. After that it runs the SELECT statement.

How safe that function is? is it possible to SQL inject it or XSS it?

Was it helpful?

Solution

This is safe. If you dont feel safe, it only has characters and integers, you can easily test it is a md5 string (see example below). But again, there is no need for all of that.

An alternative would be prepared statements. They're a bit more complex, but safe:

$stmt = $mysqli->prepare("INSERT INTO test(id) VALUES (?)");
$stmt->bind_param("s", 'a1b2c3'); // s stands for String, i would be Integer
$stmt->execute();

This is a very simplefied example, the url above the codeblock explains more. Keep in mind that prepared statement have a overhead! doing this for 1 excecution per query will slow things down.


Small example to check if a string could be a md5 hash:

function isMd5($string){
    /// md5 strings are 32chars* long. Simple test, do that first:
    if( strlen($string)!==32){ return false; }
    // It only has chars (A-F) and integers, if any other character->not md5
    elseif( preg_match("^[0-9a-f]", $string) ){        return false; }

    // No errors, return true:
    return true;
}
// *rawmode ha 16 chars, but when you work with that, you'll know 

This is not a very usefull function, because it will not really secure a lot, this is just to show you how you can verify info. You read the documentation to see the results, and make checks to test if it matches possible results.

OTHER TIPS

It's safe. But if you want to be more safe, than you can use prepared statements, which is the safest way to create queries with any equation, not just MD5: http://php.net/manual/en/mysqli.prepare.php

TL;DR

No, it isn't always safe.


Safe use requires that you…

  1. …only use it to escape SQL string literals.

    If you are using untrusted data for any other SQL token, you must protect against SQL injection in some other way. Some trivial examples:

    • attempting to use for escaping some other literal, e.g. an integer:

      # use as /script.php?id=0+OR+1
      $id = $mysqli->real_escape_string($_GET['id']);
      $mysqli->query("SELECT * FROM `users` WHERE `id` = $id")
      

      This will submit:

      SELECT * FROM `users` WHERE `id` = 0 OR 1
      
    • attempting to use for escaping an object identifier, e.g. a column name:

      # use as /script.php?col=id%60+%3D+0+OR+1+--+
      $col = $mysqli->real_escape_string($_GET['col']);
      $mysqli->query("SELECT * FROM `users` WHERE `$col` = 123")
      

      This will submit:

      SELECT * FROM `users` WHERE `id` = 0 OR 1 -- ` = 123
      
    • attempting to use for escaping SQL:

      # use as /script.php?orderby=OR+1
      $orderby = $mysqli->real_escape_string($_GET['orderby']);
      $mysqli->query("SELECT * WHERE `id` = 0 $orderby")
      

      This will submit:

      SELECT * FROM `users` WHERE `id` = 0 OR 1
      


  2. …correctly quote those escaped string literals.

    There are two possible ways of doing this. Either:

    • quote the escaped string literal with single-quote ' characters; or

    • explicitly set an SQL mode that includes neither ANSI_QUOTES nor NO_BACKSLASH_ESCAPES and then quote the escaped string literal with double-quote " characters.

    If you don't fully perform one of these two steps, you could still be vulnerable to SQL injection. For example, if the server sets NO_BACKSLASH_ESCAPES by default and you don't explicitly change from that mode:

    # use as /script.php?name=%22+OR+1+--+
    $name = $mysqli->real_escape_string($_GET['name']);
    $mysqli->query('SELECT * FROM `users` WHERE `name` = "'.$name.'"');
    

    This will submit

    SELECT * FROM `users` WHERE `name` = "" OR 1 -- "
    


  3. …only use it after first setting the encoding of your database connection.

    The same encoding must be used at both client and server ends of the connection: the best way to ensure this is to call mysqli::set_charset(). Using other methods could leave you vulnerable to encoding attacks:

    # use as /script.php?name=%bf%27+OR+1+--+
    $name = $mysqli->real_escape_string($_GET['name']);
    $mysqli->query("SET NAMES 'gbk'");  // sets only on server, not on client
    $mysqli->query("SELECT * FROM `users` WHERE `name` ='$name'");
    

    This will submit

    SELECT * FROM `users` WHERE `name` = '縗' OR 1 -- '
    


  4. …don't use a vulnerable version of the libmysqlclient library.

    Prior to that bugfix (in MySQL versions 4.1.20, 5.0.22, 5.1.11), libmysqlclient was vulnerable to the above encoding vulnerability even if the connection character encoding was properly set.

For sql injection, it is safe - it prevents mysql operators to be used in value.

I myself would validate the string beforehand with php, to give user an feedback, that something went wrong, like so:

if( ! preg_match('/^[a-f0-9]{32}$/', $md5)) {
    // show error message
}

This also protects from XSS, because you can insert only alphanumberic string.

Without php check you have two main options:

A - strip all tags from user input:

$safe_input = strip_tags($user_input);

B - Use encoded html

$safe_input = htmlentites($user_input);

And to prevent UTF-7 XSS, then specify character set at the top of your code:

header('Content-Type: text/html; charset=utf-8');

More on xss here

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top