L'injection SQL est un problème de sécurité très grave, en grande partie parce qu'il est si facile de se tromper: la façon évidente et intuitive de créer une requête intégrant les entrées utilisateur vous rend vulnérable, et la bonne façon de l'atténuer nécessite que vous connaissiez les paramètres requêtes et injection SQL en premier.
Il me semble que le moyen évident de résoudre ce problème serait de fermer l'option évidente (mais erronée): corriger le moteur de base de données de sorte que toute requête reçue qui utilise des valeurs codées en dur dans sa clause WHERE au lieu de paramètres renvoie une belle description message d'erreur vous demandant d'utiliser des paramètres à la place. Cela nécessiterait évidemment une option de désactivation afin que des choses comme les requêtes ad hoc des outils d'administration puissent toujours s'exécuter facilement, mais elles devraient être activées par défaut.
Avoir cela arrêterait l'injection SQL à froid, presque du jour au lendemain, mais pour autant que je sache, aucun SGBDR ne le fait réellement. Y a-t-il une bonne raison pour laquelle non?
bad_ideas_sql = 'SELECT title FROM idea WHERE idea.status == "bad" AND idea.user == :mwheeler'
aurait à la fois des valeurs codées en dur et paramétrées dans une seule requête - essayez d'attraper cela! Je pense qu'il existe des cas d'utilisation valables pour de telles requêtes mixtes.SELECT * FROM jokes WHERE date > DATE_SUB(NOW(), INTERVAL 1 DAY) ORDER BY score DESC;
"bad"
est vraiment littéral ou s'il résulte d'une concaténation de chaînes. Les deux solutions que je vois sont soit de se débarrasser de SQL et d'autres DSL intégrés aux chaînes (oui s'il vous plaît), soit de promouvoir des langages où la concaténation de chaînes est plus ennuyeuse que d'utiliser des requêtes paramétrées (umm, no).Réponses:
Il y a trop de cas où l'utilisation d'un littéral est la bonne approche.
Du point de vue des performances, il y a des moments où vous voulez des littéraux dans vos requêtes. Imaginez que j'ai un traqueur de bogues où une fois qu'il sera suffisamment gros pour se soucier des performances, je m'attends à ce que 70% des bogues du système soient "fermés", 20% seront "ouverts", 5% seront "actifs" et 5 % aura un autre statut. Je peux raisonnablement vouloir que la requête qui renvoie tous les bogues actifs soit
plutôt que de passer en
status
tant que variable de liaison. Je veux un plan de requête différent en fonction de la valeur transmise pourstatus
- je voudrais faire une analyse de table pour retourner les bogues fermés et une analyse d'index sur lestatus
pour renvoyer les prêts actifs. Désormais, différentes bases de données et différentes versions ont des approches différentes pour (avec plus ou moins de succès) permettre à la même requête d'utiliser un plan de requête différent en fonction de la valeur de la variable de liaison. Mais cela a tendance à introduire une quantité décente de complexité qui doit être gérée pour équilibrer la décision de déranger l'analyse d'une requête ou de réutiliser un plan existant pour une nouvelle valeur de variable de liaison. Pour un développeur, il peut être judicieux de gérer cette complexité. Ou il peut être judicieux de forcer un chemin différent lorsque j'ai plus d'informations sur l'aspect de mes données que l'optimiseur.Du point de vue de la complexité du code, il y a aussi de nombreuses fois qu'il est parfaitement logique d'avoir des littéraux dans les instructions SQL. Par exemple, si vous avez une
zip_code
colonne qui a un code postal à 5 caractères et a parfois 4 chiffres supplémentaires, il est parfaitement logique de faire quelque chose commeplutôt que de passer 4 paramètres distincts pour les valeurs numériques. Ce ne sont pas des choses qui changeront jamais, donc les rendre liées aux variables ne fait que rendre le code potentiellement plus difficile à lire et créer le potentiel que quelqu'un liera les paramètres dans le mauvais ordre et se retrouve avec un bogue.
la source
L'injection SQL se produit lorsqu'une requête est créée en concaténant du texte à partir d'une source non approuvée et non validée avec d'autres parties d'une requête. Bien qu'une telle chose se produise le plus souvent avec des littéraux de chaîne, ce ne serait pas la seule façon de se produire. Une requête pour des valeurs numériques peut prendre une chaîne saisie par l' utilisateur (qui est supposé pour ne contenir que des chiffres) et concaténer avec d' autres matériaux pour former une requête sans les guillemets normalement associés à littéraux de chaîne; le code qui fait trop confiance à la validation côté client peut avoir des choses comme les noms de champ proviennent d'une chaîne de requête HTML. Il n'y a aucun moyen que le code regardant une chaîne de requête SQL puisse voir comment elle a été assemblée.
Ce qui est important n'est pas de savoir si une instruction SQL contient des littéraux de chaîne, mais plutôt si une chaîne contient des séquences de caractères provenant de sources non fiables , et la validation pour cela serait mieux gérée dans la bibliothèque qui construit les requêtes. Il n'y a généralement aucun moyen en C # d'écrire du code qui autorisera un littéral de chaîne mais ne permettra pas d'autres types d'expression de chaîne, mais on pourrait avoir une règle de pratiques de codage qui nécessite que les requêtes soient construites à l'aide d'une classe de construction de requêtes plutôt que la concaténation de chaînes et toute personne transmettant une chaîne non littérale au générateur de requêtes doit justifier une telle action.
la source
Si vous voulez mettre les résultats de ceux-ci dans le pied de page de votre forum, vous devrez ajouter un paramètre factice juste pour dire faux à chaque fois. Ou le programmeur web naïf cherche comment désactiver cet avertissement et continue ensuite.
Vous pouvez maintenant dire que vous ajouteriez une exception pour les énumérations, mais cela ouvre à nouveau le trou (bien que plus petit). Sans oublier que les gens doivent d'abord être éduqués pour ne pas les utiliser
varchars
.Le vrai problème de l'injection est la construction par programme de la chaîne de requête. La solution pour cela est un mécanisme de procédure stockée et imposant son utilisation ou une liste blanche de requêtes autorisées.
la source
deleted = false
parNOT deleted
, ce qui évite le littéral. Mais le point est valable en général.TL; DR : Il faudrait restreindre tous les littéraux, pas seulement ceux des
WHERE
clauses. Pour des raisons pour lesquelles ils ne le font pas, il permet à la base de données de rester découplée des autres systèmes.Premièrement, votre prémisse est défectueuse. Vous souhaitez restreindre uniquement les
WHERE
clauses, mais ce n'est pas le seul endroit où l'entrée utilisateur peut aller. Par exemple,Ceci est également vulnérable à l'injection SQL:
Vous ne pouvez donc pas simplement restreindre les littéraux dans la
WHERE
clause. Vous devez restreindre tous les littéraux.Il nous reste maintenant la question: "Pourquoi autoriser les littéraux?" Gardez cela à l'esprit: bien que les bases de données relationnelles soient utilisées sous une application écrite dans une autre langue un pourcentage important du temps, il n'est pas nécessaire d' utiliser le code d'application pour utiliser la base de données. Et ici, nous avons une réponse: vous avez besoin de littéraux pour écrire du code. La seule autre alternative serait d'exiger que tout le code soit écrit dans une langue indépendante de la base de données. Les avoir vous donne donc la possibilité d'écrire du "code" (SQL) directement dans la base de données. Il s'agit d'un découplage précieux, et il serait impossible sans littéraux. (Essayez d'écrire dans votre langue préférée parfois sans littéraux. Je suis sûr que vous pouvez imaginer à quel point cela serait difficile.)
À titre d'exemple courant, les littéraux sont souvent utilisés dans la population des tables de liste de valeurs / de recherche:
Sans eux, vous auriez besoin d'écrire du code dans un autre langage de programmation juste pour remplir ce tableau. La possibilité de le faire directement en SQL est précieuse .
Il nous reste alors une autre question: pourquoi les bibliothèques clientes du langage de programmation ne le font-elles pas alors? Et ici, nous avons une réponse très simple: ils auraient ré-implémenté l'intégralité de l'analyseur de base de données pour chaque version prise en charge de la base de données . Pourquoi? Parce qu'il n'y a pas d'autre moyen de garantir que vous avez trouvé chaque littéral. Les expressions régulières ne suffisent pas. Par exemple: cela contient 4 littéraux distincts dans PostgreSQL:
Essayer de le faire serait un cauchemar de maintenance, d'autant plus que la syntaxe valide change souvent entre les principales versions des bases de données.
la source