web-dev-qa-db-fra.com

Puis-je me protéger contre l'injection SQL en échappant aux guillemets simples et aux entrées utilisateur environnantes?

Je me rends compte que les requêtes SQL paramétrées constituent 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 utilisateur et l'échappement de guillemets simples et l'entourage de guillemets simples. Voici le code:

sSanitizedInput = "'" & Replace(sInput, "'", "''") & "'"

Les guillemets simples entrés par l'utilisateur sont remplacés par des guillemets simples, ce qui empêche les utilisateurs de mettre fin à la chaîne. Tous les éléments qu'ils taperont, tels que les points-virgules, les signes de pourcentage, etc., feront tous 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 crois que la 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 entre.

Je ne vois aucun moyen de lancer une attaque par injection SQL contre cela, mais je me rends compte que si cela était aussi résistant aux balles qu'il me semble que quelqu'un d'autre y aurait déjà pensé et que 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 désinfection? Un exemple de saisie utilisateur exploitant cette technique serait très utile.


MISE À 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 échappe à une seule citation et laisse l'autre à mettre fin à la chaîne de sorte 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 du SQL dans une base de données MySQL, mais dans SQL Server 2000, le seul moyen (que j'ai pu trouver) d'échapper à une citation unique est d'utiliser une autre citation unique; les barres obliques inverses ne le feront pas.

Et à moins qu'il n'y ait un moyen d'arrêter l'échappement de guillemets simples, aucune des autres entrées de l'utilisateur ne sera exécutée car elle sera considérée comme une chaîne contiguë.

Je comprends qu’il existe de meilleurs moyens d’assainissement des entrées, mais je suis vraiment plus intéressé à comprendre pourquoi la méthode que j’ai fournie ci-dessus ne fonctionne 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 bien la voir.

129
Patrick

Tout d'abord, c'est juste une mauvaise pratique. La validation des entrées est toujours nécessaire, mais elle est aussi toujours incertaine.
Pire encore, la validation de la liste noire est toujours problématique, il est bien mieux de définir explicitement et strictement les valeurs/formats que vous acceptez. Certes, cela n’est pas toujours possible - mais dans une certaine mesure, cela doit toujours être fait.
Quelques articles 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 dans lesquelles même une évasion de citation peut être ignorée.

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 vraiment petite, vous allez devoir gérer la maintenance et peut-être une certaine gouvernance: comment vous assurez-vous qu'elle est bien faite, partout, tout le temps?

La bonne façon de le faire:

  • Validation de la liste blanche: type, longueur, format ou valeurs acceptées
  • Si vous voulez faire une liste noire, allez-y. L'échappement de devis est une bonne chose, mais dans le contexte des autres mesures d'atténuation.
  • Utiliser les objets Command et Parameter pour préparer et valider
  • Appelez les requêtes paramétrées uniquement.
  • Mieux encore, utilisez exclusivement les procédures stockées.
  • Évitez d’utiliser SQL dynamique et n’utilisez pas la concaténation de chaînes pour créer des requêtes.
  • Si vous utilisez des SP, vous pouvez également limiter les autorisations de la base de données pour exécuter uniquement les SP requis, sans accéder directement aux tables.
  • vous pouvez également facilement vérifier que l'ensemble de la base de code n'accède à la base de données que par le biais de SP ...
84
AviD

D'accord, cette réponse sera liée à la mise à jour de la question:

"Si quelqu'un connaissait un moyen spécifique de monter une attaque par injection SQL contre cette méthode de désinfection, j'aimerais bien la voir."

Maintenant, en plus de la barre oblique inversée MySQL - et compte tenu du fait que nous parlons de MSSQL, il y a en fait 3 façons possibles d'injecter encore votre code dans SQL

sSanitizedInput = "'" & Remplacer (sInput, "'", "" ") &" ""

Tenez compte du fait que ceux-ci ne seront pas tous valables à tout moment et dépendent beaucoup de votre code réel:

  1. Injection SQL de second ordre - si une requête SQL est reconstruite sur la base de données extraites de la base de données après l'échappement, les données sont concaténées sans échappement et peuvent être indirectement injectées en SQL. Voir
  2. Troncature de chaîne - (un peu plus compliqué) - Le scénario est que vous avez deux champs, par exemple un nom d'utilisateur et un mot de passe, et que le code SQL les concatène. Et les deux champs (ou juste le premier) ont une limite de longueur stricte. Par exemple, le nom d'utilisateur est limité à 20 caractères. Disons que vous avez ce code:
username = left(Replace(sInput, "'", "''"), 20)

Ensuite, ce que vous obtenez - est le nom d'utilisateur, échappé et ensuite coupé à 20 caractères. Le problème ici - je vais coller ma citation dans le 20ème caractère (par exemple après 19 a), et votre citation échappée sera réduite (dans le 21ème caractère). Puis le SQL

sSQL = "select * from USERS where username = '" + username + "'  and password = '" + password + "'"

combiné avec le nom d'utilisateur malformé mentionné ci-dessus, le mot de passe sera déjà en dehors des guillemets et contiendra simplement la charge utile directement.
3. Contrebande Unicode - Dans certaines situations, il est possible de transmettre un caractère Unicode de haut niveau qui ressemble à un guillemet, mais n'est pas - jusqu'à ce qu'il arrive à la base de données, où tout à coup il l'est . Comme ce n'est pas un devis lorsque vous le validez, il passera facilement ... Voir ma réponse précédente pour plus de détails et un lien vers des recherches originales.

39
AviD

En un mot: Ne faites jamais une requête qui vous échappe. Vous êtes lié à quelque chose qui ne va pas. 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.

27
Nick Johnson

Je me rends compte que cela fait longtemps après que la question a été posée, mais ..

Une méthode pour lancer une attaque sur la procédure 'citer l'argument' consiste à tronquer une chaîne. Selon MSDN, dans SQL Server 2000 SP4 (et SQL Server 2005 SP1), une chaîne trop longue sera discrètement 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 en dehors du tampon. Vous pouvez donc supprimer des parties d’une clause where.

Cela serait probablement surtout utile dans un scénario de page 'utilisateur admin' où vous pourriez abuser de l'instruction 'update' pour ne pas effectuer toutes les vérifications prévues.

Donc, si vous décidez de citer tous les arguments, assurez-vous de savoir ce qui se passe avec la taille des chaînes et veillez à ne pas être tronqué.

Je recommanderais d'aller avec des paramètres. Toujours. Je voudrais juste pouvoir appliquer cela dans la base de données. Et comme effet secondaire, vous avez plus de chances d’obtenir de meilleurs résultats en cache, car davantage d’instructions se ressemblent. (C'était certainement vrai sur Oracle 8)

17
Jørn Jensen

L’assainissement en entrée n’est pas quelque chose que vous voulez à demi-cul. Utilisez tout votre cul. Utilisez des expressions régulières sur les champs de texte. Essayez de relier vos données numériques au type numérique approprié et de signaler 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.

8
tom.dietrich

J'ai utilisé cette technique pour gérer la fonctionnalité de "recherche avancée", pour laquelle la création d'une requête à partir de zéro était la seule solution viable. (Exemple: autoriser l'utilisateur à rechercher des produits en fonction d'un ensemble illimité de contraintes sur les attributs de produit, en affichant les colonnes et leurs valeurs autorisées en tant que contrôles d'interface graphique afin de réduire le seuil d'apprentissage des utilisateurs.)

En soi, il est sûr, autant que je sache. Comme un autre intervenant l’a souligné, vous devrez peut-être aussi traiter l’échappement arrière (bien que ce ne soit pas le cas lorsqu’on passera la requête à SQL Server en utilisant ADO ou ADO.NET, du moins - ne peut pas attester de toutes les bases de données ou technologies).

Le problème, c'est que vous devez vraiment savoir quelles chaînes contiennent les entrées de l'utilisateur (toujours potentiellement malveillantes) et quelles chaînes sont des requêtes SQL valides. Si vous utilisez des valeurs de la base de données, l’un des pièges est-il fourni à l’origine par l’utilisateur? Si c'est le cas, ils doivent également être échappés. Ma réponse est d'essayer de désinfecter 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.

8
Pontus Gagge

C'est une mauvaise idée quand même, comme vous semblez le savoir.

Qu'en est-il de quelque chose comme échapper à la citation dans la chaîne comme ceci:\'

Votre remplacement entraînerait:\''

Si la barre oblique inverse échappe à la première citation, la deuxième citation a mis fin à la chaîne.

6
WW.

Il y a deux façons de le faire, sans exception, pour se protéger des injections SQL; déclarations préparées ou procédures mémorisées prédéfinies.

5
olle

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 partir avec la meilleure hypothèse. liste noire. De même, vous souhaitez utiliser des processus stockés paramétrés dans tout , mais encore une fois, ce n'est pas toujours possible, vous devez donc 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 bon compte rendu 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 d'en installer un véritable, faites-le. Mais ne pense pas que tu es en sécurité.

5
Invalid Character

Votre défense échouerait si:

  • la requête attend un nombre plutôt qu'une chaîne
  • il n’existait aucun autre moyen de représenter un guillemet simple, notamment:
    • une séquence d'échappement telle que\039
    • un caractère unicode

(dans ce dernier cas, cela devrait être quelque chose qui a été développé seulement après que vous ayez fait votre remplacement)

4
AJ.

Ouais, cela devrait fonctionner jusqu'à ce que quelqu'un lance SET QUOTED_IDENTIFIER OFF et utilise une double citation sur vous.

Edit: il n’est pas aussi simple que de ne pas autoriser l’utilisateur malveillant à désactiver les identifiants cités:

Le pilote ODBC de SQL Server Native Client et le fournisseur de base de données OLE de SQL Server pour SQL Server définissent automatiquement QUOTED_IDENTIFIER sur ON lors de la connexion. Cela peut être configuré dans ODBC sources de données, dans ODBC attributs de connexion ou OLE propriétés de connexion à la base de données. La valeur par défaut de SET QUOTED_IDENTIFIER est OFF pour les connexions à partir d'applications DB-Library.

Lorsqu'une procédure stockée est créée, le Les paramètres SET QUOTED_IDENTIFIER et SET ANSI_NULLS sont capturés et utilisés pour les appels ultérieurs de cette procédure stockée..

SET QUOTED_IDENTIFIER également correspond au paramètre QUOTED_IDENTIFER de ALTER DATABASE.

SET QUOTED_IDENTIFIER est mis au moment de l'analyse. Définir au moment de l'analyse signifie que si l'instruction SET est présente dans le traitement par lots ou la procédure stockée, elle prend effet, que l'exécution du code atteigne réellement ce point ou non. et l'instruction SET prend effet avant l'exécution d'éventuelles instructions.

QUOTED_IDENTIFIER peut être désactivé de différentes manières sans que vous le sachiez nécessairement. Certes, ce n'est pas l'exploit fumeur que vous recherchez, mais c'est une très grande surface d'attaque. Bien sûr, si vous avez également échappé aux guillemets, nous sommes de retour à notre point de départ. ;)

4
Mark Brackett

Patrick, ajoutez-vous 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, alors vous avez une exposition.

4
Rob Kraft

Si vous avez des requêtes paramétrées disponibles, vous devriez les utiliser à tout moment. Il suffit qu’une requête glisse à travers le filet et votre base de données est en danger.

4
Kev

Quel code moche toute cette désinfection de la saisie de l'utilisateur serait! Ensuite, le StringBuilder clunky pour l'instruction SQL. La méthode des instructions préparées produit un code bien plus propre, et les avantages de l’injection SQL sont un ajout vraiment intéressant.

Aussi, pourquoi réinventer la roue?

1
JeeBee

Plutôt que de changer une citation en (à quoi ressemble) deux citations simples, pourquoi ne pas simplement la changer en une apostrophe, une citation ou la supprimer complètement?

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 souvienne 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.

1
Kevin Fairchild

Cela pourrait fonctionner, mais cela me semble un peu hokey. Je vous recommande de vérifier que chaque chaîne est valide en la testant plutôt avec une expression régulière.

0
Rob

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 font que passer des nombres (une simple vérification consiste à savoir si elle peut être analysée comme étant int/double/décimale?).

C'est beaucoup de travail supplémentaire.

0
Joseph Daigle