Mongodb is vulnerable to SQL injection in PHP at least

Its a common misconception that as MongoDB does not use SQL it is not vulnerable to SQL injection attacks. PHP uses objects rather than SQL to pass queries to the MongoDB server; for example the following script selects an item form MongoDB where the username equals ‘bob’ and the password equals ‘password’.

$collection->find(array(
    "username" => $_GET['username'],
    "passwd" => $_GET['passwd']
));

This is equivalent to the SQL syntax

mysql_query("SELECT * FROM collection
    WHERE username=" . $_GET['username'] . ",
    AND passwd=" . $_GET['passwd'])

In a normal SQL injection attack we can replace either of the two input parameters with a string such that the SQL query always returns true. e.g.

login.php?username=admin&passwd=" OR 1 --

That wont work with MongoDB; however if we can pass in an object to the PHP MongoDB driver we could alter the query in a similar fashion. Luckily PHP provides us with a way to pass objects as GET or POST parameters:

login.php?username=admin&passwd[$ne]=1

This creates the MongoDB query

$collection->find(array(
    "username" => "admin",
    "passwd" => array("$ne" => 1)
));

Which is the equivalent to the following SQL statement which, unless the password is “1″ will always return true.

mysql_query("SELECT * FROM collection
    WHERE username="admin",
    AND passwd!=1

The solution is to ensure your variables are properly typed before they are passed into the MongoDB driver. The following code is not vulnerable to MongoDB injection:

$collection->find(array(
    "username" => (string)$_GET['username'],
    "passwd" => (string)$_GET['passwd']
));
This entry was posted in MongoDB and tagged , , , . Bookmark the permalink.

5 Responses to Mongodb is vulnerable to SQL injection in PHP at least

  1. Michael Grech says:

    Hey Phil, thanks for the post. Would this work as well than? Note the double quotes.

    “username” => “$_GET['username'] “

  2. Michael Grech says:

    Ah answered my own question. So you could do something like the following as well:

    $username = $_GET['username']

    than…

    “username” => “$username”

    • phil says:

      Your code would work but this would also work and means you don’t have to declare $username.

      “username” => “{$_GET['username']}”

      Either way as long as the input to mongodb remains a string your safe :)

  3. Andrew says:

    Nice catch! Just a suggestion though: never access $_GET directly. Always use a getter function, to which developer MUST specify the expected format. This approach solves most of the web application security problems.

    For instance:
    “username” => get(‘username’, GET_STRING, ‘/^[a-zA-Z0-9]+$/’)
    (regex is optional)

    Or:
    “username” => get_string(‘username’, ‘/^[a-zA-Z0-9]+$/’)

    Function get() should be the only way to access $_GET. You should even rename $_GET to make sure developers obey this convention.

    My 0.02 EUR.

  4. witty says:

    well Andrew ,
    But where can we find the code of the functions :
    get_string()
    and/or
    get()

    I can’t find them in the http://php.net/manual/ !

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>