Les injections SQL - Protection

Tester l'existant

Pour tester un champ spécifique dans une application, il suffit le plus souvent d'y insérer des caractères spéciaux du SQL, comme « ' », « " », etc. En général, si le champ est sensible à l'injection SQL, un message d'erreur va être affiché. Très souvent, ce message contient la requête qui a été exécutée, ce qui facilite le travail de l'attaquant en lui donnant des indices sur le schéma de la base.

Il est également possible de tester tout un site, soit manuellement en regardant dans le code source de l'application si les accès à la base de données sont sécurisés, soit de l'extérieur en utilisant des applications comme IBM Rational AppScan.

Exemple de rapport d'AppScan sur le site http://demo.testfire.net :
Rapport d'appscan (Au passage, on peut noter qu'AppScan ne se contente pas de tester les injections SQL: il détecte également bien d'autres failles, comme les failles XSS, par exemple)

Champ sensible à l'injection
Champ sensible à l'injection




Corriger le code

Mais concrètement, comment se protège-t-on des injections SQL ?

En fait, il y a plusieurs méthodes plus ou moins contraignantes. Nous allons ici présenter ces diverses techniques en évoquant leurs avantages et inconvénients.


L'échappement de caractère

C'est la méthode la plus plébiscité sur internet; elle consiste à appliquer sur les entrées de l'utilisateur une fonction qui va échapper les caractères spéciaux du SQL. Par exemple, « ' » sera remplacé par « \' » et l'apostrophe ne sera donc pas interprété comme une fin de chaîne par le SGBD.

Par exemple, PHP met à disposition une fonction "addslashes" qui fait ce traitement. Toujours en PHP, il est possible d'activer l'option "magic_quote_gpc" dans le php.ini, ce qui aura pour effet d'appeler la fonction addslashes automatiquement sur toutes les entrées de l'utilisateur.

Malgré le fait qu'elle soit la plus connue, cette méthode n'est pas optimale:


Les fonctions de vérification

Le principe est simple: on vérifie avec une fonction "maison", pour chacune des entrées de l'utilisateur, si ce qu'il a tapé correspond bien à ce qui est attendu. Par exemple, si on demande un numéro de sécurité sociale, on vérifie que notre variable contient bien 15 chiffres, et rien d'autre.

Cette méthode n'est pas optimale non plus:


La fonction 'prepare'

C'est LA bonne méthode pour se prémunir des injections SQL.

Une injection est rendue possible par le fait que notre script côté serveur HTTP n'envoie la requête SQL qu'après l'avoir "remplie" avec les entrées utilisateurs: le SGBD va donc essayer d'interpréter cette requête-ci, dont le sens à peu être été altéré au moment de l'ajout des entrées utilisateur.

Avec l'instruction "prepare", on envoie la macro au SGBD, qui va interpréter la requête puis attendre les paramètres (les entrées utilisateur) avant de l'exécuter. Quand on envoie au SGBD les paramètres, la requête est déjà compilée, donc quel que soit le contenu de ces derniers, il ne sera jamais considéré comme un bout SQL de la requête mais comme une simple valeur à tester.

L'instruction "prepare" s'utilise comme suit (on reprend notre exemple de chercheur de livre):

<?php
{…}
	//On demande au SGBD de préparer (de précompiler) la requête
	//Le " ? " indique qu'il y aura un paramètre à cet endroit
	$smt = $db->prepare ( "SELECT * FROM livres WHERE isbn = ?");
	
	//On renseigne les paramètres 
	$stmt->bindParam(1,$_GET['isbn']) ;

	//On exécute la requête
	$stmt->execute() ;
{...} 
?>
			

Cette technique offre plusieurs avantages:




Bonnes pratiques

Quelques principes à retenir pour éviter les injections SQL, et limiter le pouvoir de l'attaquant en cas d'exploitation: