views:

589

answers:

4

I am reviewing a Linux based perl web application that contains a login handler with the ubiquitous

my $sth = $DB->prepare("SELECT password from passwords where userid='$userid'") or die; $sth->execute or die; ...

where $userid is initialized from (unsafe, unfiltered) web user input.

It is well known that the DBI documentation recommends that this code should be changed to use the placeholder "?" in place of '$userid' for security.

This code was isolated on an off network box, as-is, for the purpose of a security review. Code like this on an internet server will eventually be cracked as there are bots now that scan for this vulnerability. The access control is also ineffective for protecting anything important because known injections can delete databases, insert bad data or new users, or bypass the access control to allow entry to the web application.

As the application can be configured to use either PostgreSQL or MySQL and questions were raised about comparative vulnerability I tried out both databases and tested each configuration with some SQL injection attempts.

Under PostgreSQL an input of '; do bad stuff here; and here; would crash the login cgi as expected and execute the bad stuff.

What was unexpected was that MySQL resisted this attack. This got me to wonder if there was a setting of some sort for DBD::MySQL or elsewhere that limited prepare to 1 statement per call, or was MySQL resistant in some other way.

As I understand it MySQL is not SQL-injection resistant in general.

This is not a question purely about techniques for eliminating SQL injection; for that perhaps see http://stackoverflow.com/questions/2200256/how-can-i-avoid-sql-injection-attacks.

The question is: Is MySQL somehow more resistant than PostgreSQL to SQL injection attack under the PERL DBI and why might this be the case?

+7  A: 

The MySQL client library seems to limit to one statement per call by default (I encountered it with PHP).

But that's shouldn't be a reason to use MySQL over PostgreSQL, since you can still inject by using subqueries.

laurentb
MySQL supports multiple statement as of 4.1. A lot of clients have not followed suit, or they leave it off by default to preserve compatibility. DBD::mysql allows the execution of multiple statements if you turn on *mysql_multi_statements*. So the thin protection of MySQL's single-statement processing is one line added by a maintenance programmer away from disappearing.
Schwern
A: 

It might be difficult, if not impossible, to have a generic sanitizer against SQL injections.

Added [As commented, using the DBI correctly and with the help of the DB's client library, the injections can certainly be minimized with respect to preparing the SQL statement. However, it's important to keep in mind that sanitizing user input also involves application logic which is independent of the DB used. For example, using another user's credentials may provide a valid and safe statement, but with unintended consequences. Anyway, that's going further than the question asked.]

Removed [You're better off sanitizing the input yourself rather than having any sense of false security on the client's resistance to these types of attacks. Not that you shouldn't use them, just don't assume they provide more than minimal help against the attacks.]

Ioan
I don't believe this is true on any level. You're almost always better not to develop security measures by yourself. It is perfectly safe and sane to assume a correctly formulated prepare (With `?`) is secure against a SQL injection.
Evan Carroll
Attempts to sanitize the input yourself are paradoxically less secure than just letting the database do it. Why? The database knows definitively what is and is not a special character, you do not. A bind parameter puts a definitive quote jail around its value. Attempts to write a SQL value escaper will miss a special value, for some database, for some version, possibly a future version. For example, some databases now accept multiple Unicode quotes. Read "SQL Smuggling" for details. http://seclists.org/bugtraq/2008/Sep/109
Schwern
-1: Parametrized queries/SQL placeholders are completely generic and provide absolute protection against SQL injection attacks because they send the SQL commands (the prepared query) and the data to operate on (the parameters) separately. Since the two are not mixed, there is *no way* to trick the database into executing the data as commands.
Dave Sherohman
I apologize for the misunderstanding and poor wording of my answer. My intent was equal to the answer provided by *Bob Jarvis*. I believe this is the first I've seen anyone state a programmer should not sanitize user input and instead depend on a third party library for all of it.
Ioan
@loan Tough crowd. Don't let it get you down. Some of this is semantics -- sanitise has come to mean removing/checking for bad input (which is wishful thinking) instead of checking for good input and using only (which is easier to judge as working). Checking for good input is fine for integers or state codes but if someone is named O'Brian those placeholders are needed not just for security but for the application to work properly.
Paul
@Paul I agree removing/checking for bad input is wrong, checking for good input is right. Sanitizing user input involves more than simply checking for escape characters; you must also check logic, but that goes beyond the DBI in question. I'll update the answer accordingly.
Ioan
Actually, for any database except MySql, it's just about doing Replace(string, '''', ''''''). This is, however, of course not a reason to not use parameterized queries.
erikkallen
+11  A: 

Guarding against injection attacks is not the responsibility of the database, it's the responsibility of the developer. If the developer writes code that creates queries by concatenating strings derived from user input the resulting queries will be vulnerable to injection attacks, and all the code spent on sanitization, etc, is IMHO a waste of time. If the code is written to use parameterized queries, and user input is relegated to being used as parameter values, the resulting queries will be reasonably safe from injection attacks. (And I'd be interested in hearing how it might be possible to do an injection attack through a parameter value).

Share and enjoy.

Bob Jarvis
AMENsql injection is just perfectly legitimate sql queries running against a database. really the whole question here needs to remove the databases and focus solely on libraries
+3  A: 

No, in fact, MySQL is almost categoricially less secure, in this case it appears as if the prepare statements are not done on the server at all.

Prepared statement support (server side prepare) As of 3.0002_1, server side prepare statements were on by default (if your server was >= 4.1.3). As of 3.0009, they were off by default again due to issues with the prepared statement API (all other mysql connectors are set this way until C API issues are resolved). The requirement to use prepared statements still remains that you have a server '>= 4.1.3'

To use server side prepared statements, all you need to do is set the variable mysql_server_prepare in the connect:

$dbh = DBI->connect( "DBI:mysql:database=test;host=localhost;mysql_server_prepare=1", "", "", { RaiseError => 1, AutoCommit => 1 } );

  • Note: delimiter for this param is ';'

There are many benefits to using server side prepare statements, mostly if you are performing many inserts because of that fact that a single statement is prepared to accept multiple insert values.

To make sure that the 'make test' step tests whether server prepare works, you just need to export the env variable MYSQL_SERVER_PREPARE:

export MYSQL_SERVER_PREPARE=1

Presumably, they are prepared on the server in PostgreSQL.

So far as security is concerned, you're simply doing it wrong: use ? as you've said. Otherwise you're just exploiting that Postgres can prepare multiple statements: and that isn't something negative. I believe MySQL can probably do this too, the only difference here is DBD::MySQL is claiming that C API issues preclude the use of the server-side prepares so they're relying on some other source as being authoritative for the server. Right now DBD::MySQL is probably using C function in a mysql library that predates MySQL having server-side prepares (pre 4.1.3 is my guess).

Evan Carroll