SQL Injection Protection in PHP With PDO

Database abstraction layers like PHP's Portable Datayour code relies on platform-specific features like
Objects (PDO) are not a new concept, but a lot oftriggers and stored procedures, but if you're not relying
developers don't seem to realise the security benefiton them at all and you're just doing simple INSERT
they're getting for free by using them - inherentUPDATE/DELETE operations it's a free ride. Sounds
protection against SQL injection.moderately useful, but nothing exciting, right? Right.
SQL injection is the buffer overflow of the webAnother neat feature invented a long time ago is
application world - it's been around forever, and everyprepared statements, and most database abstraction
web application developer should know how to writelayers (including PDO) implement this as a way to
secure code that's not vulnerable to it. For those not inperform the same query multiple times with different
the know, SQL injection is a technique whereby adata sets (e.g. inserting a whole bunch of new rows).
malicious attacker can exploit inadequate dataNow, when building statements with PDO, instead of
validation to inject arbitrary SQL code into yourbuilding the SQL string manually as demonstrated
application's queries and have it executed as though itearlier, we build the statement with placeholders like
is a legitimate query. I won't go too deeply into SQLthis:
injection in this article, but here's a simple example:$sql = "INSERT INTO fruits (name, price) VALUES (?,
The front page of your application has a login form,?)";and then execute the query with a data set passed
which is submitted to a PHP script to validate theto the abstraction layer as follows:
user's credentials and allow or deny access to the$sth = $dbh->prepare($sql);
application. The login form submits two variables by$sth->execute(array($fruit, $price));
POST asWhen the data is handed to PDO like this, it then either
follows:username=fred&password=Fr3dRul3zpasses the data on to the database driver directly, or
The POSTed data is then used to build an SQL querybuilds the query internally in a safe manner with any
to validate the credentials, like this:potentially malicious data encoded or escaped. As you
$sql = "SELECT * FROM users WHERE username =can see, this is an easy way around the problem of
'".$_REQUEST['username']."' AND password =SQL injection.
'".$_REQUEST['password']."'";However, prepared statements with PDO aren't all
This would result in the SQL query:puppies and rainbows. Using prepared statements can
SELECT * FROM users WHERE username = 'fred'introduce a number of interesting caveats of which
AND password = 'Fr3dRul3z'developers should be aware. For example, in the
Assuming a row exists in the database with theseMySQL client API prepared statements can not
credentials, the user would be allowed to log in. Anexecute certain types of queries[1] and they do not
attacker could easily circumvent this authenticationuse the query cache[1][2] which may have an impact
scheme by escaping out of the username field into theon your application's performance.
SQL query by entering nothing into the password fieldThe inherent security in using prepared statements
and this into the username field:sounds great, but developers should not let PDO and
' OR 1==1 --other abstraction layers/prepared statement
The resulting SQL query string would look like this:implementations lull them into a false sense of security.
SELECT * FROM users WHERE username = 'fred'Untrusted data should always be validated and
OR 1==1 -- ' AND password = ''sanitised, PDO is just another line of defense. It doesn't
Which, as I'm sure you can see, would select all userscover the territory of a multitude of other input
from the database as the condition 1==1 will always bevalidation vulnerabilities like cross site scripting, but it
true. The rest of the query is discarded with thedoes do a good job of protecting applications against
comment operator '--'. The way to avoid this kind ofSQL injection. The best strategy is only allowing known
attack is to sanitise the data submitted to the form bygood data by whitelisting characters and matching
escaping everything that could be used to escape theinput data against regular expression patterns, then
confines of the quotes around the fields (e.g.using prepared statements to catch anything SQL
mysql_real_escape_string() if you're using MySQL).injection-wise that the input validation misses, all in
However, in a land far away somebody was inventingconjunction with a web application firewall like
database abstraction layers...ModSecurity.
The primary objective of database abstraction layersPDO has been built in to PHP since version 5.1.0, which
like PDO is clean abstraction in your code away fromwas released in Nov 2005. Unless you've got a good
the database platform - so, theoretically, you couldreason for not using it in your PHP apps, you should be
switch database platforms from, say, MySQL to- it is a portable replacement for the old mysql_*
PostgreSQL or Oracle with minimal changes to thefunctions and other platform-specific functions with the
code. In practice this depends heavily on how muchadded benefit of protection against SQL injection.