Une injection SQL survient lorsqu'un attaquant parvient à insérer du code SQL arbitraire dans une requête destinée à la base de données, en profitant d'un champ de formulaire, d'un paramètre d'URL, ou de tout autre point d'entrée dont le contenu est directement intégré à la requête sans précaution. Cette faille, connue depuis des décennies, reste pourtant l'une des plus fréquemment exploitées sur le web, notamment sur des développements PHP sur mesure où la requête SQL est construite manuellement plutôt que via un framework qui impose une protection par défaut.

Ce qui rend une requête vulnérable

Le problème apparaît dès qu'une valeur fournie par l'utilisateur est directement concaténée dans une chaîne de caractères formant la requête SQL. Si un champ de recherche accepte n'importe quel texte et que ce texte est inséré tel quel dans la requête, un visiteur malveillant peut saisir une valeur qui modifie la structure logique de la requête elle même, plutôt que de simplement fournir une donnée à rechercher. Cette manipulation peut permettre de contourner une authentification, d'extraire des données auxquelles l'utilisateur ne devrait pas avoir accès, ou dans les cas les plus graves, de modifier ou supprimer des données de la base.

Pourquoi l'échappement manuel ne suffit pas

Une réaction courante face à ce risque consiste à tenter de nettoyer ou échapper manuellement les caractères spéciaux avant de les insérer dans la requête, par exemple en échappant les apostrophes. Cette approche reste fragile : elle dépend de la mémoire et de la rigueur du développeur sur chaque point d'entrée du site, et un seul oubli suffit à rouvrir la faille. Elle peut également se révéler incomplète selon le contexte d'utilisation de la donnée dans la requête, ce qui en fait une protection incertaine plutôt qu'une solution robuste.

Les requêtes préparées, une séparation stricte entre code et donnée

Les requêtes préparées, disponibles nativement en PHP via les extensions PDO ou MySQLi, résolvent ce problème à la racine en séparant complètement la structure de la requête SQL des valeurs qui y sont insérées. La requête est d'abord envoyée à la base de données sous une forme fixe, avec des emplacements réservés pour les valeurs variables, puis ces valeurs sont transmises séparément et ne peuvent en aucun cas être interprétées comme du code SQL, quel que soit leur contenu. Un attaquant peut saisir n'importe quelle chaîne de caractères, y compris des instructions SQL complètes, ces caractères resteront traités comme une simple donnée textuelle sans effet sur la structure de la requête.

Un exemple concret de la différence

Une requête construite par concaténation directe ressemble typiquement à une chaîne assemblée avec la variable utilisateur insérée au milieu du texte SQL. Une requête préparée avec PDO sépare cette logique : la requête contient un espace réservé, généralement représenté par un point d'interrogation ou un nom précédé de deux points, et la valeur réelle est fournie séparément lors de l'exécution, via une méthode dédiée de l'objet de connexion. Cette différence structurelle, une fois comprise, ne demande pas plus de lignes de code qu'une concaténation manuelle, contrairement à une idée reçue parfois répandue.

Ce que les requêtes préparées ne couvrent pas

Les requêtes préparées protègent spécifiquement contre l'injection SQL, mais ne dispensent pas des autres bonnes pratiques de sécurité applicative, comme la validation du format attendu d'une donnée avant même de l'envoyer à la base, ou l'échappement approprié des données affichées dans une page HTML pour se prémunir contre un autre type de faille appelé injection de script. Un formulaire qui accepte un email doit par exemple vérifier que la donnée ressemble à un email avant traitement, indépendamment de la protection apportée par la requête préparée elle même.

Le cas des ORM et frameworks modernes

Les frameworks PHP courants et les bibliothèques ORM utilisent des requêtes préparées en interne par défaut, ce qui protège automatiquement le développeur tant qu'il utilise les méthodes fournies par ces outils plutôt que de construire une requête brute manuellement au sein de ces mêmes frameworks. Sur un développement entièrement sur mesure sans framework, la responsabilité de systématiser les requêtes préparées repose entièrement sur les habitudes de codage adoptées dès le début du projet, ce qui rend d'autant plus important d'en faire une règle non négociable plutôt qu'une option ponctuelle.

Points à vérifier avant publication : confirmer que la description du fonctionnement de PDO et MySQLi reste exacte pour les versions de PHP actuellement en support actif, et vérifier que la distinction entre injection SQL et injection de script correspond bien à des mécanismes de protection distincts, sans les confondre dans la formulation.