Comment éviter les injections SQL en PHP ?

Réponses rédigées par Antoine
Dernière mise à jour : 2019-12-21 15:13:18
Thèmes : webmaster - sql - php - securite
Question

Comment protéger son code et éviter les injections SQL en PHP ?

Réponse

Quelque soit le type de bases de données que vous utilisez, l'une des toutes premières chose à faire pour éviter les injections SQL en PHP, est de correctement définir et contrôler vos variables, surtout si celles-ci sont issues d'un formulaire en ligne.

Concernant les requêtes SQL elles-mêmes, et pour éviter les injections SQL en PHP, vous devez utiliser des instructions préparées et des requêtes paramétrées lors de vos requêtes SQL. Ce sont des instructions SQL qui sont envoyées et analysées par le serveur de base de données séparément de tout autre paramètre. De cette façon, il est impossible pour un attaquant d'injecter du SQL.

Vous avez essentiellement deux options pour parvenir à éviter les injections SQL en PHP :

  • L'utilisation de PDO : PDO est compatible avec toutes les bases de données du type SQL.

exemple d'une requête SQL avec PDO :

$query = $pdo->prepare('SELECT * FROM table WHERE nom = :nom');

$query->execute(array('nom' => $nom));

foreach ($query as $row) {
    // ...
}
  • L'utilisation de MySQLi : MySQLi n'est compatible qu'avec les bases de données du type MySQL.

exemple d'une requête SQL avec MySQLi:

$query = $dbConnection->prepare('SELECT * FROM table WHERE nom = ?');
$query->bind_param('s', $nom);

$query->execute();

$result = $query->get_result();
while ($row = $result->fetch_assoc()) {
    // ...
}

Remarque : bind_param définit le type de variable : ici s pour string : une chaîne de caractères.

Si vous vous connectez à une base de données autre que MySQL, il existe une seconde option spécifique au pilote à laquelle vous pouvez vous référer : pg_prepare() et pg_execute() pour PostgreSQL. PDO reste cependant la méthode universelle.

Vous devez également vous assurer que les requêtes de connexion à la base sont correctement effectuées.

Lorsque vous utilisez PDO pour accéder à une base de données MySQL, les instructions préparées ne sont pas utilisées par défaut. Pour résoudre ce problème, vous devez désactiver l'émulation des instructions préparées.

exemple de création d'une connexion à l'aide de PDO :

$db = new PDO('mysql:dbname=ma_base;host=127.0.0.1;charset=utf8', 'user', 'password');
$db ->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$db ->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

Dans l'exemple ci-dessus, le mode d'erreur n'est pas strictement nécessaire, mais il est conseillé de l'ajouter. De cette façon, le script ne s'arrêtera pas avec une erreur fatale en cas de problème.

Ce qui est cependant obligatoire est la première ligne setAttribute() ; elle indique à PDO de désactiver les instructions préparées émulées et d'utiliser de véritables instructions préparées. Cela garantit que l'instruction et les valeurs ne sont pas analysées par PHP avant de les envoyer au serveur MySQL ; ce qui protège également contre d'éventuelles injections SQL.

Si pour certaines raisons, généralement l'utilisation d'anciennes versions de PHP, vous êtes dans l'obligation d'utiliser de vieilles méthodes de requêtes SQL, il vous faudra absolument utiliser mysql_real_escape_string, ainsi que mettre en place une fonction de sécurisation.

exemple de fonction pour sécuriser les variables PHP avec mysql_real_escape_string :

function securite_bdd($string)
{
	if(ctype_digit($string))
		{
		$string = intval($string);
		}
	else
		{
		$string = mysql_real_escape_string($string);
		$string = addcslashes($string, '%_');
		}
	return $string;
}

Pour d'avantages d'informations sur les techniques d'injection SQL, référez-vous à cet excellent article de Christophe Grenier.