Comment éviter une injection SQL en PHP ?

Pour prévenir les injections SQL, il faut faire appel aux requêtes préparées. Le point sur cette technique.

Les injections SQL sont une technique courante pour récupérer des informations contenues dans une base de données ou endommager un site internet. Elle consiste à insérer du code SQL dans des champs. Lorsque ces champs sont ensuite insérés en base, le code SQL est interprété et une nouvelle requête est envoyée à la base de données, ce qui permet d'obtenir, de modifier ou de supprimer des informations. Le langage PHP permet heureusement de se protéger contre ce type d'attaque.

Lorsque l'on effectue une requête de manière simple avec MySQLi (ou un autre gestionnaire de base de données), les données sont directement interprétées. Il est possible par exemple pour une recherche d'insérer du code malicieux.

//Requête de départ
mysql_query(SELECT FROM salaries WHERE prenom = . $prenom);
//On peut injecter du code SQL en complétant la requête de cette façon
$prenom = 'Jean';DELETE FROM salaries
mysql_query(SELECT FROM salaries WHERE prenom = . $prenom);
//La requête va alors effacer toutes les lignes de la table salaries

Pour prévenir les injections SQL, il faut faire appel aux requêtes préparées. Ce sont des requêtes dans lesquels les paramètres sont interprétés indépendamment de la requête elle-même. De cette manière, il est impossible d'effectuer des injections. Dans tous les systèmes de gestion de bases de données, deux méthodes sont utilisées : prepare() qui prépare la requête et execute() qui exécute la requête avec les paramètres.

//Exemple avec PDO (fonctionne sur toutes les bases de données)
$stmt = $pdo->prepare('SELECT * FROM salaries WHERE prenom = :prenom');
$stmt->execute(array('prenom' => $prenom));
foreach ($stmt as $ligne)
{
//Traitement de la ligne de résultat
}
//Exemple avec MySQLi (fonctionne pour les bases de données MySQL)
$stmt = $connexion->prepare('SELECT * FROM salaries WHERE prenom = ?');
//Avec MySQLi, c'est la méthode bind_param() qui lie les champs aux paramètres
$stmt->bind_param('s', $prenom);
$stmt->execute();
$resultat = $stmt->get_result();
while ($ligne = $resultat->fetch_assoc())
{
//Traitement de la ligne de résultat
}

Si vous utilisez PDO sur une base de données MySQL, il faut savoir que par défaut les requêtes préparées ne sont pas réelles, elles sont émulées par PDO. Pour forcer PDO à utiliser des requêtes préparées réelles, il faut le préciser après la connexion à la base de données.

$connexion = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'utilisateur', 'motDePasse');
$connexion ->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
//La ligne suivante indique à PDO de bien générer une erreur fatale si un problème survient. On peut détecter et gérer les problèmes de cette manière.
$connexion ->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

PHP