“El objetivo del arte es representar no la apariencia externa de las cosas, sino su significado interior.”
Aristóteles

Usar prepared statements y parameterized. Esto son sentencias SQL preparadas que se envían a la base de datos de forma separada a cualquier parámetro. De esta forma es imposible para un atacante inyectar SQL malicioso. Es la forma más recomendable y segura de evitar este tipo de ataques. Se puede hacer de dos formas:

1. Con PDO:

$stmt = $pdo->prepare(‘SELECT * FROM usuarios WHERE nombre = :nombre’); $stmt->execute(array(‘nombre’ => $nombre)); foreach ($stmt as $row) { // Hacer algo con $row }

2. Con MySQLi:

$stmt = $dbConnection->prepare(‘SELECT * FROM usuarios WHERE nombre = ?’); $stmt->bind_param(‘s’, $nombre); $stmt->execute(); $result = $stmt->get_result(); while ($row = $result->fetch_assoc()){ // Hacer algo con $row }

Si se utiliza PostgreSQL existen opciones como _pgprepare() y _pgexecute() si se quiere hacer mediante esta segunda opción. PDO es una opción universal que vale para todas las bases de datos.

Cuando se usa una conexión PDO a MySQL los prepared statements de verdad puede que no se utilicen por defecto. Para usarlos siempre, hay que desactivar la emulación de prepared statements. La conexión se ha de hacer así:

$db = new PDO(‘mysql:dbname=test;host=localhost;charset=utf8’, ‘usuario’, ‘contraseña’); $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

El error mode no es estrictamente necesario, pero es aconsejable ponerlo. De esta forma el script no parará con un Fatal Error cuando algo sale mal, y da la posibilidad al desarrollador de capturar (catch) cualquier error lanzado (throw) como PDOExceptcion.

Configurar ATTR_EMULATE_PREPARES a false puede ser importante ya que le dice a PDO que deshabilite los prepared statements emulados y sólo utilice los de verdad (por ejemplo en caso de que el driver no pueda preparar satisfactoriamente la sentencia).

De esta forma la sentencia y los valores no son analizados por PHP antes de ser enviados al servidor de bases de datos, sino que son analizados y compilados por éste, eliminando cualquier posibilidad de inyectar SQL malicioso. Los valores de los parámetros se combinan con una sentencia ya compilada, no son un string SQL. La inyección SQL funciona de forma que se inyecta el SQL malicioso en el string antes de ser enviado al servidor de bases de datos, por lo que enviando de forma separada la sentencia de los parámetros se reduce mucho el riesgo.

Otra forma es Escapar caracteres especiales. La función _mysqli_real_escapestring, o _mysqli::escapestring y _mysqli::real_escapestring en su versión OOP, coge el string que va a ser pasado a la sentencia y lo devuelve con los posibles ataques SQL injection eliminados. Ejemplo usando su versión OOP:

$mysqli = new mysqli(“localhost”, “usuario”, “contraseña”, “database”);

$usuario = “‘ OR 1′”;
$usuario = $mysqli->escape_string($usuario);

$query1 = “SELECT * FROM user WHERE name = ‘$usuario'”;
echo “SQL injection escapado: <br>” . $query1 . “<br>”;

$usuario2 = “‘; DELETE FROM customers WHERE 1 or username = ‘”;
$usuario2 = $mysqli-&gt;real_escape_string($usuario2);

$query2 = “SELECT * FROM user WHERE name = ‘$usuario2′”;
echo “SQL injection escapado: <br>” . $query2;

El resultado lo devuelve con las comillas simples ‘ escapadas.

Función addslashes(). Esta es una solución que ya no se utiliza pues es bastante vulnerable. Lo que hace la función addslashes() es escapar el string mediante barras. Los strings escapados son ‘, «, \ y el byte null. Si por ejemplo insertas una barra \ en medio de un carácter múltiple en concreto, la barra pierde su valor ya que es parte de ese carácter múltiple, pudiéndose insertar una comilla después. Aquí hay una explicación más detallada.

La mejor opción es usar prepared statements y parameterized queries. De todas formas siempre es mejor combinar este tipo de precauciones con otras como data validation, indicando expresamente el tipo de variable que se espera (integer, por ejemplo).