Je me rends compte que les requêtes SQL paramétrées sont le moyen optimal de nettoyer les entrées utilisateur lors de la création de requêtes contenant des entrées utilisateur, mais je me demande ce qui ne va pas avec la saisie des entrées utilisateur et en échappant aux guillemets simples et en entourant la chaîne entière avec des guillemets simples. Voici le code:
sSanitizedInput = "'" & Replace(sInput, "'", "''") & "'"
Tout guillemet simple saisi par l'utilisateur est remplacé par des guillemets simples doubles, ce qui élimine la possibilité pour les utilisateurs de terminer la chaîne, de sorte que tout ce qu'ils peuvent taper, comme des points-virgules, des signes de pourcentage, etc., fera partie de la chaîne et pas réellement exécuté dans le cadre de la commande.
Nous utilisons Microsoft SQL Server 2000, pour lequel je pense que le guillemet simple est le seul délimiteur de chaîne et le seul moyen d'échapper au délimiteur de chaîne, il n'y a donc aucun moyen d'exécuter quoi que ce soit que l'utilisateur tape.
Je ne vois aucun moyen de lancer une attaque par injection SQL contre cela, mais je me rends compte que si c'était aussi infaillible qu'il me semble, quelqu'un d'autre y aurait déjà pensé et ce serait une pratique courante.
Quel est le problème avec ce code? Existe-t-il un moyen de faire passer une attaque par injection SQL au-delà de cette technique de nettoyage? Un exemple d'entrée utilisateur exploitant cette technique serait très utile.
METTRE À JOUR:
Je ne connais toujours aucun moyen de lancer efficacement une attaque par injection SQL contre ce code. Quelques personnes ont suggéré qu'une barre oblique inverse échapperait à un guillemet simple et laisserait l'autre pour terminer la chaîne afin que le reste de la chaîne soit exécuté dans le cadre de la commande SQL, et je me rends compte que cette méthode fonctionnerait pour injecter SQL dans une base de données MySQL, mais dans SQL Server 2000, le seul moyen (que j'ai pu trouver) d'échapper à un guillemet simple est d'utiliser un autre guillemet simple; les barres obliques inverses ne le feront pas.
Et à moins qu'il n'y ait un moyen d'arrêter l'échappement du guillemet simple, aucune des autres entrées utilisateur ne sera exécutée car tout sera considéré comme une chaîne contiguë.
Je comprends qu'il existe de meilleures façons de nettoyer les entrées, mais je suis vraiment plus intéressé à savoir pourquoi la méthode que j'ai fournie ci-dessus ne fonctionnera pas. Si quelqu'un connaît un moyen spécifique de monter une attaque par injection SQL contre cette méthode de désinfection, j'aimerais le voir.
Réponses:
Tout d'abord, c'est juste une mauvaise pratique. La validation des entrées est toujours nécessaire, mais elle est également toujours incertaine.
Pire encore, la validation de la liste noire est toujours problématique, il est préférable de définir explicitement et strictement les valeurs / formats que vous acceptez. Certes, ce n'est pas toujours possible - mais dans une certaine mesure, cela doit toujours être fait.
Quelques travaux de recherche sur le sujet:
Le fait est que toute liste noire que vous faites (et les listes blanches trop permissives) peut être contournée. Le dernier lien vers mon article montre des situations où même les échappements de citation peuvent être contournés.
Même si ces situations ne s'appliquent pas à vous, c'est toujours une mauvaise idée. De plus, à moins que votre application ne soit trivialement petite, vous allez devoir gérer la maintenance, et peut-être un certain niveau de gouvernance: comment vous assurer que c'est bien fait, partout et tout le temps?
La bonne façon de le faire:
la source
sp_executesql
au lieu deEXEC
). Autrement dit, vous pouvez générer dynamiquement votre instruction SQL tant qu'aucun texte concaténé ne provient de l'utilisateur. Cela présente également des avantages en termes de performances;sp_executesql
prend en charge la mise en cache.D'accord, cette réponse portera sur la mise à jour de la question:
Maintenant, en plus de l'échappement de la barre oblique inverse MySQL - et en tenant compte du fait que nous parlons en fait de MSSQL, il existe en fait 3 façons possibles de continuer à injecter SQL votre code
Tenez compte du fait que ceux-ci ne seront pas tous valides à tout moment et dépendent beaucoup du code réel qui l'entoure:
Ensuite, ce que vous obtenez - est le nom d'utilisateur, échappé, puis réduit à 20 caractères. Le problème ici - je vais coller ma citation dans le 20e caractère (par exemple après 19 a), et votre citation d'échappement sera coupée (dans le 21e caractère). Puis le SQL
combiné avec le nom d'utilisateur malformé susmentionné, le mot de passe se trouvera déjà en dehors des guillemets et contiendra simplement la charge utile directement.
3. Contrebande Unicode - Dans certaines situations, il est possible de passer un caractère Unicode de haut niveau qui ressemble à une citation, mais qui ne l'est pas - jusqu'à ce qu'il arrive à la base de données, où il se trouve soudainement . Puisqu'il ne s'agit pas d'un devis lorsque vous le validez, il se déroulera facilement ... Voir ma réponse précédente pour plus de détails et un lien vers la recherche originale.
la source
En un mot: ne faites jamais de requête qui vous échappe. Vous êtes obligé de vous tromper. Utilisez plutôt des requêtes paramétrées ou, si vous ne pouvez pas le faire pour une raison quelconque, utilisez une bibliothèque existante qui le fait pour vous. Il n'y a aucune raison de le faire vous-même.
la source
Je me rends compte que c'est longtemps après que la question a été posée, mais ..
Une façon de lancer une attaque sur la procédure 'quote the argument' est d'utiliser la troncature de chaîne. Selon MSDN, dans SQL Server 2000 SP4 (et SQL Server 2005 SP1), une chaîne trop longue sera silencieusement tronquée.
Lorsque vous citez une chaîne, la taille de la chaîne augmente. Chaque apostrophe est répétée. Cela peut ensuite être utilisé pour pousser des parties du SQL hors du tampon. Ainsi, vous pourriez effectivement supprimer des parties d'une clause where.
Cela serait probablement surtout utile dans un scénario de page «administrateur utilisateur» où vous pourriez abuser de l'instruction «update» pour ne pas faire toutes les vérifications qu'elle était censée faire.
Donc, si vous décidez de citer tous les arguments, assurez-vous de savoir ce qui se passe avec les tailles de chaîne et veillez à ce que vous ne rencontriez pas de troncature.
Je recommanderais d'aller avec des paramètres. Toujours. J'aimerais juste pouvoir appliquer cela dans la base de données. Et en tant qu'effet secondaire, vous êtes plus susceptible d'obtenir de meilleurs hits de cache, car plus d'instructions se ressemblent. (C'était certainement vrai sur Oracle 8)
la source
J'ai utilisé cette technique pour gérer la fonctionnalité de «recherche avancée», où la création d'une requête à partir de zéro était la seule réponse viable. (Exemple: permettre à l'utilisateur de rechercher des produits en fonction d'un ensemble illimité de contraintes sur les attributs du produit, en affichant les colonnes et leurs valeurs autorisées en tant que contrôles GUI pour réduire le seuil d'apprentissage pour les utilisateurs.)
En soi, il est sûr AFAIK. Cependant, comme un autre répondant l'a souligné, vous devrez peut-être également gérer l'échappement de retour arrière (mais pas lors du passage de la requête à SQL Server à l'aide d'ADO ou ADO.NET, au moins - ne peut pas garantir toutes les bases de données ou technologies).
Le hic, c'est que vous devez vraiment être certain quelles chaînes contiennent une entrée utilisateur (toujours potentiellement malveillante) et quelles chaînes sont des requêtes SQL valides. L'un des pièges est si vous utilisez des valeurs de la base de données - ces valeurs ont-elles été fournies à l'origine par l'utilisateur? Si tel est le cas, ils doivent également être échappés. Ma réponse est d'essayer de nettoyer le plus tard possible (mais pas plus tard!), Lors de la construction de la requête SQL.
Cependant, dans la plupart des cas, la liaison de paramètres est la voie à suivre - c'est simplement plus simple.
la source
L'assainissement des entrées n'est pas quelque chose que vous voulez faire à moitié. Utilisez tout votre cul. Utilisez des expressions régulières sur les champs de texte. TryCast vos numériques au type numérique approprié et signalez une erreur de validation si cela ne fonctionne pas. Il est très facile de rechercher des modèles d'attaque dans votre entrée, tels que '-. Supposons que toutes les entrées de l'utilisateur sont hostiles.
la source
flavors
dans cette page).C'est une mauvaise idée de toute façon comme vous semblez le savoir.
Qu'en est-il de quelque chose comme échapper à la citation dans une chaîne comme celle-ci: \ '
Votre remplacement entraînerait: \ ''
Si la barre oblique inverse échappe au premier guillemet, le deuxième a terminé la chaîne.
la source
Réponse simple: cela fonctionnera parfois, mais pas tout le temps. Vous voulez utiliser la validation de la liste blanche sur tout ce que vous faites, mais je me rends compte que ce n'est pas toujours possible, vous êtes donc obligé de choisir la meilleure liste noire. De même, vous voulez utiliser des procs stockés paramétrés dans tout , mais encore une fois, ce n'est pas toujours possible, vous êtes donc obligé d'utiliser sp_execute avec des paramètres.
Il existe des moyens de contourner toute liste noire utilisable que vous pouvez créer (et certaines listes blanches aussi).
Un article décent est ici: http://www.owasp.org/index.php/Top_10_2007-A2
Si vous avez besoin de le faire comme solution rapide pour vous donner le temps de mettre en place un vrai, faites-le. Mais ne pensez pas que vous êtes en sécurité.
la source
Il y a deux façons de le faire, sans exception, pour être à l'abri des injections SQL; instructions préparées ou procédures stockées pré-paramétrées.
la source
Si vous disposez de requêtes paramétrées, vous devez les utiliser à tout moment. Tout ce qu'il faut, c'est qu'une seule requête glisse sur le net et votre base de données est en danger.
la source
Oui, cela devrait fonctionner jusqu'à ce que quelqu'un exécute SET QUOTED_IDENTIFIER OFF et utilise un guillemet double sur vous.
Edit: Ce n'est pas aussi simple que de ne pas permettre à l'utilisateur malveillant de désactiver les identifiants entre guillemets:
QUOTED_IDENTIFIER peut être désactivé de nombreuses façons sans que vous le sachiez nécessairement. Certes, ce n'est pas l'exploit que vous recherchez, mais c'est une assez grande surface d'attaque. Bien sûr, si vous avez également échappé des guillemets doubles, nous revenons là où nous avons commencé. ;)
la source
Votre défense échouerait si:
(dans ce dernier cas, il faudrait que ce soit quelque chose qui n'a été développé qu'après avoir effectué votre remplacement)
la source
Patrick, est-ce que vous ajoutez des guillemets simples autour de TOUTES les entrées, même numériques? Si vous avez une entrée numérique, mais ne mettez pas les guillemets simples autour d'elle, vous avez une exposition.
la source
Quel vilain code serait tout ce nettoyage des entrées utilisateur! Puis le StringBuilder maladroit pour l'instruction SQL. La méthode de l'instruction préparée donne un code beaucoup plus propre et les avantages de SQL Injection sont un ajout vraiment intéressant.
Aussi pourquoi réinventer la roue?
la source
Plutôt que de changer un guillemet simple en (à quoi ressemble) deux guillemets simples, pourquoi ne pas simplement le changer en une apostrophe, une citation ou le supprimer entièrement?
Quoi qu'il en soit, c'est un peu un kludge ... surtout quand vous avez légitimement des choses (comme des noms) qui peuvent utiliser des guillemets simples ...
REMARQUE: votre méthode suppose également que tout le monde travaillant sur votre application se souvient toujours de nettoyer les entrées avant qu'elles n'atteignent la base de données, ce qui n'est probablement pas réaliste la plupart du temps.
la source
Bien que vous puissiez trouver une solution qui fonctionne pour les chaînes, pour les prédicats numériques, vous devez également vous assurer qu'ils ne transmettent que des nombres (une vérification simple est peut-il être analysé comme int / double / décimal?).
C'est beaucoup de travail supplémentaire.
la source
Cela pourrait fonctionner, mais cela me semble un peu hokey. Je recommanderais de vérifier que chaque chaîne est valide en la testant par rapport à une expression régulière à la place.
la source
Oui, vous pouvez, si ...
Après avoir étudié le sujet, je pense que les entrées nettoyées comme vous l'avez suggéré sont sûres, mais uniquement selon ces règles:
vous ne permettez jamais aux valeurs de chaîne provenant des utilisateurs de devenir autre chose que des littéraux de chaîne (c'est-à-dire éviter de donner l'option de configuration: "Entrez des noms / expressions de colonne SQL supplémentaires ici:"). Types de valeur autres que les chaînes (nombres, dates, ...): convertissez-les dans leurs types de données natifs et fournissez une routine pour le littéral SQL de chaque type de données.
vous utilisez soit
nvarchar
/nchar
colonnes (et préfixez les littéraux de chaîne avecN
) OU limitez les valeurs entrant dansvarchar
/char
colonnes aux caractères ASCII uniquement (par exemple, lancez une exception lors de la création d'une instruction SQL)vous validez toujours la longueur de la valeur pour qu'elle corresponde à la longueur réelle de la colonne (jetez une exception si elle est plus longue)
vous vous assurez que
SET QUOTED_IDENTIFIER
c'est toujoursON
En respectant ces 4 points, vous devez être en sécurité. Si vous violez l'un d'entre eux, un moyen d'injection SQL s'ouvre.
la source