Prevent SQL injections 💉 in PHP using prepared statements and parameterized queries

Prevent SQL injections 💉 in PHP using prepared statements and parameterized queries

Recently I did security and pen-testing assessment of a web application written entirely in PHP, and some of the biggest issues I’ve come along were in-code vulnerabilities to šŸ’‰ SQL injections.

To fix this, I recommended using prepared statements and parameterized queries which are basically SQL statements that are sent to and parsed by the database server separately from any parameters. This way it is impossible for an attacker to inject malicious SQL.

You basically have two options to achieve this:

Connect to database with PDO in PHP

I’d recommend usingĀ PDOĀ (PHP Data Objects) to run parameterized SQL queries as not only does this protect against SQL injection, but it also speeds up queries.

And by using PDO rather thanĀ mysql_,Ā mysqli_, andĀ pgsql_Ā functions, you make your application a little more abstracted from the database, in the rare occurrence that you have to switch database providers.

$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');

$stmt->execute([ 'name' => $name ]);

foreach ($stmt as $row) {
    // Do something with $row
}

Using MySQLi

$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'

$stmt->execute();

$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
    // Do something with $row
}

If you’re connecting to a database other than MySQL, there is a driver-specific second option that you can refer to (for example,Ā pg_prepare()Ā andĀ pg_execute()Ā for PostgreSQL). PDO is the universal option.

See alsoĀ  WordPress SMTP settings via functions.php

PDO with Prepared Statements

When usingĀ PDOĀ to access a MySQL databaseĀ prepared statements areĀ not used by default. To fix this you have to disable the emulation of prepared statements. An example of creating a connection using PDO is:

$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'password');

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

In the above example the error mode isn’t strictly necessary,Ā but it is advised to add it. This way the script will not stop with aĀ Fatal ErrorĀ when something goes wrong. And it gives the developer the chance toĀ catchĀ any error(s) which areĀ thrown asĀ PDOExceptions.

What isĀ mandatory, however, is the firstĀ setAttribute()Ā line, which tells PDO to disable emulated prepared statements and useĀ realĀ prepared statements. This makes sure the statement and the values aren’t parsed by PHP before sending it to the MySQL server (giving a possible attacker no chance to inject malicious SQL).

Although you can set theĀ charsetĀ in the options of the constructor, it’s important to note that ā€˜older’ versions of PHP (before 5.3.6)Ā silently ignored the charset parameterĀ in the DSN.

BONUS: SQL Injection Cheat Sheet

SQL Injection Cheat Sheet
click on the image to visit the SQL injection CheatSheet

The SQL Injection Cheat Sheet published by Ferruh Mavituna is a great resource that will allow you to get the basic idea of a potential attack and almost every section includes brief information about itself.

Join the discussion

I enjoy constructive responses and professional comments to my posts, and invite anyone to comment or link to my site.