Chaque réponse ici ne couvre qu'une partie du problème. En fait, il y a quatre différentes parties de requête que l'on peut ajouter au SQL de manière dynamique : -
- une chaîne
- un nombre
- un identifiant
- un mot clé syntaxique
Et les déclarations préparées ne couvrent que deux d'entre elles.
Mais parfois, nous devons rendre notre requête encore plus dynamique, en ajoutant des opérateurs ou des identificateurs. Nous aurons donc besoin de techniques de protection différentes.
En général, une telle approche de protection est basée sur liste blanche .
Dans ce cas, chaque paramètre dynamique doit être codé en dur dans votre script et choisi dans cet ensemble. Par exemple, pour faire une commande dynamique :
$orders = array("name", "price", "qty"); // Field names
$key = array_search($_GET['sort'], $orders)); // if we have such a name
$orderby = $orders[$key]; // If not, first one will be set automatically.
$query = "SELECT * FROM `table` ORDER BY $orderby"; // Value is safe
Pour faciliter le processus, j'ai écrit un fonction d'aide à la liste blanche qui fait tout le travail en une seule ligne :
$orderby = white_list($_GET['orderby'], "name", ["name","price","qty"], "Invalid field name");
$query = "SELECT * FROM `table` ORDER BY `$orderby`"; // sound and safe
Il existe un autre moyen de sécuriser les identifiants - l'échappement - mais je préfère m'en tenir à la liste blanche, qui constitue une approche plus robuste et plus explicite. Cependant, tant que vous avez un identifiant cité, vous pouvez échapper au caractère de citation pour le rendre sûr. Par exemple, par défaut pour mysql, vous devez doublez le caractère quote pour l'échapper . Les règles d'échappement sont différentes pour les autres SGBD.
Cependant, il y a un problème avec les mots-clés de la syntaxe SQL (tels que AND
, DESC
et autres), mais l'inscription sur liste blanche semble être la seule approche possible dans ce cas.
Une recommandation générale peut donc être formulée comme suit
- Toute variable qui représente un littéral de données SQL (ou, pour faire simple, une chaîne SQL ou un nombre) doit être ajoutée par le biais d'une instruction préparée. Aucune exception.
- Toute autre partie de la requête, telle qu'un mot clé SQL, un nom de table ou de champ, ou un opérateur, doit être filtrée par une liste blanche.
Mise à jour
Bien qu'il y ait un accord général sur les meilleures pratiques en matière de protection contre les injections SQL, il y a encore beaucoup de mauvaises pratiques. Et certaines d'entre elles sont trop profondément ancrées dans l'esprit des utilisateurs de PHP. Par exemple, sur cette même page, il y a (bien qu'invisible pour la plupart des visiteurs) plus de 80 réponses supprimées - toutes supprimées par la communauté en raison de leur mauvaise qualité ou de la promotion de pratiques mauvaises et dépassées. Pire encore, certaines des mauvaises réponses ne sont pas supprimées, mais prospèrent au contraire.
Par exemple, là (1) sont(2) encore(3) beaucoup(4) réponses(5) y compris le deuxième réponse la plus votée vous suggère d'utiliser l'échappement manuel des chaînes de caractères, une approche dépassée dont il est prouvé qu'elle n'est pas sûre.
Ou il y a une réponse légèrement meilleure qui suggère juste une autre méthode de formatage des chaînes de caractères et s'en vante même comme de la panacée ultime. Alors que, bien sûr, ce n'est pas le cas. Cette méthode n'est pas meilleure que le formatage ordinaire des chaînes de caractères, mais elle en conserve tous les inconvénients : elle ne s'applique qu'aux chaînes de caractères et, comme tout autre formatage manuel, il s'agit essentiellement d'une mesure facultative, non obligatoire, sujette à toutes sortes d'erreurs humaines.
Je pense que tout cela à cause d'une très vieille superstition, soutenue par des autorités telles que OWASP o le manuel PHP qui proclame l'égalité entre tout ce qui est "échappement" et la protection contre les injections SQL.
En dépit de ce que le manuel de PHP dit depuis des lustres, *`_escape_string` ne sécurise en aucun cas les données** et n'a jamais eu l'intention de le faire. En plus d'être inutile pour toute partie du SQL autre que la chaîne de caractères, l'échappement manuel est erroné, car il est manuel par opposition à automatisé.
Et l'OWASP aggrave encore la situation en insistant sur l'échappement. entrée utilisateur ce qui est un non-sens total : il ne devrait pas y avoir de tels mots dans le contexte de la protection contre les injections. Toute variable est potentiellement dangereuse, quelle que soit sa source ! Ou, en d'autres termes, chaque variable doit être correctement formatée pour être insérée dans une requête, quelle que soit la source. C'est la destination qui compte. Dès qu'un développeur commence à séparer les moutons des chèvres (en se demandant si telle ou telle variable est "sûre" ou non), il fait le premier pas vers le désastre. Sans parler du fait que même la formulation suggère l'échappement en vrac au point d'entrée, ressemblant à la fonction magique des guillemets - déjà méprisée, dépréciée et supprimée.
Donc, contrairement à ce qui se passe avec les "échappées", les déclarations préparées est la mesure qui protège effectivement contre l'injection SQL (le cas échéant).