Blog SQLFix
Requêtes paramétrées vs SQL généré
Un modèle qui écrit une requête ayant l'air correcte n'a pas résolu l'injection. La propriété de sécurité qui compte réside dans la façon dont la requête rencontre les données, et c'est le rôle des requêtes paramétrées avec des instructions préparées. Le texte généré est un point de départ pour la revue ; il ne remplace pas la limite que les instructions préparées imposent entre le code et les données.
Ce qu'une instruction préparée fait réellement
Lorsque le pilote reçoit une demande de préparation, il envoie le texte SQL au serveur. Le serveur analyse le texte, crée un plan d'exécution et stocke ce plan. Plus tard, lorsque l'application appelle l'exécution, le pilote n'envoie que les valeurs de paramètre. Le serveur applique le plan stocké à ces valeurs sans réanalyser le texte SQL. Puisque les valeurs ne deviennent jamais partie de la chaîne d'instruction, une charge utile telle que ' OR 1=1-- est traitée comme une valeur littérale, et non comme partie de la commande.
J'ai vu cela fonctionner sous MySQL 5.0, PostgreSQL 8.0, SQL Server 2005 et Oracle 9i. Chaque langage principal fournit une API de liaison : l'PreparedStatement de JDBC, le cursor.execute de Python avec des espaces réservés, le database/sql.Query de Go et le pool.execute de Node. Le modèle peut sortie la forme SELECT * FROM orders WHERE customer_id =? AND status =? ; le code environnant fournit les deux valeurs à l'exécution.
Si vous voyez la citation à l'intérieur du texte de l'instruction, vous n'utilisez pas une véritable instruction préparée ; vous faites probablement une interpolation de chaîne côté client, ce qui ne donne pas la sécurité contre les injections.
Pourquoi le SQL examiné nécessite encore des paramètres
L'examen humain peut confirmer que la forme de la requête correspond à la règle commerciale prévue. Il peut détecter les jointures manquantes, les noms de colonnes incorrects ou les filtres inefficaces. Cependant, les valeurs qui alimentent ces espaces réservés proviennent encore de sources externes : formulaires web, charges utiles API, files d'attente de messages ou fichiers hérités. Ces sources ne sont pas sous le contrôle direct du développeur au moment de l'exécution.
La feuille de triche de prévention des injections SQL d'OWASP indique clairement : « Option de défense 1 : Instructions préparées (Requêtes paramétrées) ». La feuille de triche ne liste pas « l'approbation humaine » comme substitut. L'approbation traite de la correction logique ; la paramétrisation traite de la limite de confiance entre le code et les données. Abandonner l'un ou l'autre laisse l'autre sans protection.
Même si l'examinateur humain a vu une instruction INSERT sensée, l'injection s'est produite à l'étape de liaison des valeurs, que l'examen n'a pas couverte.
De cette façon, le travail de l'examinateur reste précieux, et l'étape d'exécution conserve sa garantie de sécurité.
Les procédures stockées ne sont pas une sécurité automatique
De nombreuses équipes supposent que le déplacement de la logique dans une procédure stockée élimine le risque d'injection. Cette hypothèse est erronée lorsque la procédure elle-même construit du SQL dynamique.
Considérez une procédure écrite en T‑SQL :
CREATE PROCEDURE GetOrders @Customer NVARCHAR(50) AS BEGIN DECLARE @sql NVARCHAR(MAX) = N'SELECT * FROM Orders WHERE Customer = ''' + @Customer + ''''; EXEC sp_executesql @sql; END Si @Customer contient '); DROP TABLE Orders;--, la chaîne résultante devient un lot valide qui supprime la table. L'enveloppe de la procédure n'a pas protégé les données ; la concaténation à l'intérieur de la procédure l'a fait.
Le même modèle apparaît dans PL/SQL avec EXECUTE IMMEDIATE, dans PostgreSQL avec EXECUTE format, et dans MySQL avec PREPARE/EXECUTE construit à partir d'une variable.
Après la réécriture, exécutez la procédure avec une valeur qui contient une guillemet et vérifiez que l'erreur est une erreur de correspondance de type de données, et non une erreur de syntaxe.
Garder la génération et l'exécution comme des couches séparées
L'architecture la plus sûre traite le modèle d'IA comme un moteur de suggestions, et non comme un composant d'accès aux données. Le modèle produit une forme de requête candidate. Un humain ou une passerelle de stratégie automatisée examine cette forme pour vérifier son exactitude, sa conformité et ses performances. Ce n'est qu'après approbation que le code d'application prend la forme, la prépare avec le pilote et lie les paramètres en temps réel.
Puisque le modèle n'a jamais accès à une connexion active, il ne peut pas être trompé pour révéler des détails de schéma via des messages d'erreur. Puisque la couche d'exécution ne fait jamais confiance aux chaînes brutes, même un modèle compromis ne peut pas causer d'injection.
Ce processus garde la sortie du modèle dans la boucle de révision et garantit que l'étape d'exécution n'utilise que des instructions paramétrées.
Ce que cela n'est pas
Je veux être clair sur les limites de l'approche décrite ci-dessus. Elle ne garantit pas que chaque bogue possible sera détecté. Elle ne promet pas une protection de 100 % contre toutes les classes de failles. Elle ne prétend pas éliminer le besoin d'autres défenses telles que la validation des entrées, l'encodage de sortie ou les comptes de base de données avec les privilèges minimum.
Ce que la méthode fait, c'est :
Si vous vous fiez uniquement à la sortie du modèle et que vous sautez l'étape de révision ou de liaison de paramètres, vous perdez la protection. Si vous autorisez la couche d'exécution à concaténer des valeurs après préparation, vous réintroduisez le risque. La technique est un composant d'une stratégie de défense en profondeur, et non une solution miracle autonome.
Exemple de workflow dans le monde réel
Imaginez un produit SaaS qui permet aux gestionnaires d'exporter une liste de factures en retard. Le produit utilise SQLForm pour rédiger la requête.
Cette trace montre comment l'étape de génération reste indépendante de l'étape d'exécution, et comment la limite de paramètre bloque l'injection même lorsque l'utilisateur tente de faire passer du SQL dans un champ que le modèle ne voit jamais.
Écueils courants et comment nous les évitons
Même avec une conception solide, les équipes tombent parfois dans des habitudes qui affaiblissent la protection. Ci-dessous sont les erreurs fréquentes que j'ai observées et les mesures concrètes que nous prenons pour maintenir la sécurité du système.
Certains développeurs construisent SELECT * FROM tags WHERE tag IN (?, ?, ?) en créant d'abord une chaîne avec le bon nombre de placeholders, puis en concaténant les valeurs directement dans le SQL avant de le préparer. Cela contrecarre l'objectif car les valeurs deviennent partie intégrante du texte de l'instruction.
Une instruction préparée pour SELECT * FROM orders WHERE status = ? est exécutée plus tard avec une valeur qui contient en réalité une nouvelle instruction SELECT entière, en comptant sur le pilote pour traiter la valeur comme des données. Même si la valeur est sécurisée par des guillemets, la logique de l'application peut être incorrecte car le développeur a supposé que l'instruction pouvait gérer n'importe quel SQL.
Les tableaux de bord internes contournent parfois la passerelle de révision humaine car l'équipe suppose que les données sont fiables. Plus tard, un compte de service est compromis et commence à alimenter des charges utiles malveillantes dans le tableau de bord.
Pour le débogage, les équipes enregistrent parfois les valeurs exactes des paramètres. Si ces journaux sont exposés, ils peuvent divulguer des données sensibles telles que des mots de passe ou des informations personnelles identifiables.
Un ORM peut proposer une méthode qui semble accepter des paramètres, mais sous le capot, il construit une chaîne et appelle ExecuteNonQuery sur une chaîne SQL brute.
En surveillant ces écueils et en appliquant les correctifs correspondants, la séparation entre la génération et l'exécution reste solide, et la limite paramétrée continue de faire le travail pour lequel elle a été conçue.
Références
https://cheatsheetseries.owasp.org/cheatsheets/LLM_Prompt_Injection_Prevention_Cheat_Sheet.html
https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
https://cwe.mitre.org/data/definitions/798.html
https://cwe.mitre.org/data/definitions/89
FAQ
Non. L'injection dépend de la manière dont les valeurs répondent à la requête, et non de savoir si la requête est logiquement correcte. Les requêtes paramétrées maintiennent les valeurs séparées du texte de commande, quel que soit le contenu des valeurs.
Pas automatiquement. Une procédure qui concatène les entrées et les exécute avec EXECUTE IMMEDIATE peut toujours injecter. La sécurité vient du lien de paramètre à l'intérieur de la procédure.
Un ORM n'est sécurisé que lorsque vous utilisez son interface paramétrée. Construire une chaîne et la passer à l'ORM réintroduit l'injection. La frontière entre le code et les données doit toujours être respectée.
Non. Séparez la génération et l'exécution : le modèle élabore un brouillon, une passerelle approuve, et le code d'application lie les paramètres à l'exécution. Le modèle ne devrait jamais être le chemin d'exécution.
Essayez le produit : SQLForm
2026-09-16 · sources primaires uniquement · pas de métriques de trafic fabriquées
Articles liés
Langage naturel vers SQL : vérifications de sécurité qui comptent
Explorez la matrice complète des sujets de micro-SaaS IA →
Un requête correcte protège-t-elle contre les injections SQL ?
Non. L'injection dépend de la façon dont les valeurs répondent à la requête, et non du fait que la requête soit logiquement correcte. Les requêtes paramétrées maintiennent les valeurs séparées du texte de commande, quel que soit le contenu des valeurs.
Les procédures stockées sont-elles sécurisées par défaut ?
Pas automatiquement. Une procédure qui concatène les entrées et les exécute avec EXECUTE IMMEDIATE peut toujours injecter. La sécurité vient du lien de paramètres à l'intérieur de la procédure.
Un ORM peut-il remplacer les requêtes paramétrées ?
Un ORM est sécurisé uniquement lorsque vous utilisez son interface paramétrée. La construction d'une chaîne et son passage à l'ORM réintroduit l'injection. La limite entre le code et les données doit toujours être respectée.
Un modèle doit-il jamais détenir la connexion à la base de données ?
Non. Gardez la génération et l'exécution séparées : le modèle élabore, une passerelle approuve, et le code d'application lie les paramètres au moment de l'exécution. Le modèle ne doit jamais être le chemin d'exécution.
Uniquement pour soutenir la décision. Ne remplace pas l'examen du DBA ou les tests de charge de production. Aucune garantie de performance.