Puis-je me protéger contre l'injection SQL en évitant les guillemets simples et les entrées utilisateur adjacentes?

StackOverflow https://stackoverflow.com/questions/139199

Question

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 la chaîne entière avec des 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. 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 dans lequel 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 résistant aux balles qu'il me semble quelqu'un d'autre y aurait déjà pensé et que ce serait une pratique courante. Ma question est la suivante: qu'est-ce qui ne va pas avec ce code? Est-ce que quelqu'un connaît un moyen d'obtenir 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:

Merci à tous pour leurs réponses; à peu près toutes les informations que j'ai trouvées dans mes recherches sont apparues quelque part sur cette page, ce qui témoigne de l'intelligence et des compétences des personnes qui ont pris le temps de leur temps chargé pour m'aider à résoudre ce problème.

La raison pour laquelle je n’ai pas encore accepté de réponse, c’est que je ne connais toujours pas de 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 MS SQL 2000, le seul moyen (que j'ai pu trouver) d'échapper à un guillemet simple est d'utiliser un autre single-qoute; 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 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.

Était-ce utile?

La solution

Tout d’abord, c’est une mauvaise pratique. La validation des entrées est toujours nécessaire, mais elle est toujours aussi risquée.
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, ce 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 toutes les listes noires que vous créez (et les listes blanches trop permissives) peuvent être ignorées. 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 vous concernent pas, 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 moment?

La bonne façon de le faire:

  • Validation de la liste blanche: type, longueur, format ou valeurs acceptées
  • Si vous souhaitez créer une liste noire, continuez. L'échappement de devis est bon, mais dans le contexte des autres mesures d'atténuation.
  • Utiliser les objets Command et Parameter pour préparer et valider
  • Appeler uniquement les requêtes paramétrées.
  • Mieux encore, utilisez exclusivement les procédures stockées.
  • Évitez d'utiliser du SQL dynamique et n'utilisez pas de 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 nécessaires, sans accéder directement aux tables.
  • vous pouvez également facilement vérifier que l'ensemble de la base de code n'accède qu'à la base de données par le biais de SP ...

Autres conseils

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 de toujours injecter du code dans votre code SQL

  

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

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 avoir échappé , les données sont concaténées non échappées et peuvent être injectées indirectement dans 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é, puis ajusté à 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 + "'"

associé au nom d'utilisateur mal formé mentionné ci-dessus, le mot de passe sera déjà en dehors des guillemets et contiendra juste la charge utile directement.
 3. Contrebande Unicode - Dans certaines situations, il est possible de transmettre un caractère Unicode de haut niveau qui ressemble à une citation, mais n'est pas - tant qu'il ne parvient pas à la base de données, où tout à coup il est . Comme il ne s’agit pas d’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 les recherches originales.

En bref: 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.

Je me rends compte que la question a été posée longtemps après, 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 très utile dans un scénario de page "utilisateur admin" dans lequel 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 dans la taille des chaînes et veillez à ce que vous ne rencontriez pas de troncature.

Je recommanderais d’utiliser 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. (Cela était certainement vrai sur Oracle 8)

L’assainissement en entrée n’est pas quelque chose que vous voulez à moitié. 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 soient hostiles.

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 que je puis. Comme un autre intervenant l'a fait remarquer, vous devrez peut-être également traiter l'échappement arrière (bien que ce ne soit pas le cas lorsque la requête est transmise à SQL Server via ADO ou ADO.NET, du moins - vous ne pouvez pas garantir 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. L'une des interruptions est si vous utilisez des valeurs de la base de données. Ces valeurs étaient-elles fournies à 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 - elle est simplement plus simple.

C'est une mauvaise idée, 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 au premier guillemet, le deuxième guillemet a mis fin à la chaîne.

Réponse simple: Cela fonctionnera parfois, mais pas tout le temps. Vous souhaitez utiliser la validation de la liste blanche sur tout , mais je réalise que ce n'est pas toujours possible. Vous êtes donc obligé de choisir la meilleure liste noire. De même, vous souhaitez utiliser des procédures stockées paramétrées dans tout , mais là encore, ce n'est pas toujours possible. Vous devez donc utiliser sp_execute avec des paramètres.

Il y a des façons de contourner toute liste noire utilisable que vous pouvez créer (et quelques listes blanches aussi).

Vous trouverez une bonne description de la situation: 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 pensez pas que vous êtes en sécurité.

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.

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

Oui, cela devrait fonctionner jusqu'à ce que quelqu'un exécute SET QUOTED_IDENTIFIER OFF et utilise une citation double sur vous.

Modifier: il n’est pas aussi simple que de ne pas autoriser un utilisateur malveillant à désactiver les identificateurs cités:

  

Le pilote ODBC SQL Server Native Client et le fournisseur OLE DB SQL Server Native Client pour SQL Server définissent automatiquement QUOTED_IDENTIFIER sur ON lors de la connexion. Cela peut être configuré dans des sources de données ODBC, des attributs de connexion ODBC ou des propriétés de connexion OLE DB. 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, 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 a également correspond au paramètre QUOTED_IDENTIFER de ALTER DATABASE.

     

SET QUOTED_IDENTIFIER est défini 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é sans que vous le sachiez. Certes, ce n’est pas l’exploit de pistolet fumant 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. ;)

Votre défense échouera si:

  • la requête attend un nombre plutôt qu'une chaîne
  • il n'y avait 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, il faudrait que ce soit quelque chose qui a été développé seulement après que vous ayez fait votre remplacement)

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

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 beaucoup plus propre et les avantages de l’injection SQL constituent un ajout vraiment intéressant.

Aussi pourquoi réinventer la roue?

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 tous ceux qui travaillent sur votre application se souviennent toujours de nettoyer les entrées avant qu'elles n'apparaissent dans la base de données, ce qui n'est probablement pas réaliste la plupart du temps.

Bien que vous puissiez trouver une solution qui fonctionne pour les chaînes, vous devez également vous assurer que les prédicats numériques ne renvoient que des nombres (une simple vérification peut-elle être analysée sous la forme int / double / decimal?).

C'est beaucoup de travail supplémentaire.

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

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 les règles suivantes:

  1. vous ne permettez jamais aux valeurs de chaîne provenant d'utilisateurs de devenir autre chose que des littéraux de chaîne (c'est-à-dire d'éviter de donner l'option de configuration: " entrez d'autres noms / expressions de colonnes SQL ici: "). Types de valeur autres que des chaînes (nombres, dates, ...): convertissez-les en types de données natifs et fournissez une routine pour le littéral SQL à partir de chaque type de données.

    • La validation des instructions SQL pose problème
  2. vous utilisez soit nvarchar / nchar colonnes (et les chaînes de préfixe avec N ) OU les valeurs limites entrant dans varchar / char uniquement en caractères ASCII (par exemple, une exception lors de la création d'une instruction SQL)

    • Ainsi, vous éviterez la conversion automatique des apostrophes de CHAR (700) à CHAR (39) (et peut-être d’autres hacks Unicode similaires)
  3. vous validez toujours la longueur de la valeur pour l'adapter à la longueur réelle de la colonne (une exception est générée si elle est plus longue)

    • SQL Server permettait de contourner une erreur SQL générée lors de la troncature (entraînant une troncature silencieuse)
  4. vous vous assurez que SET QUOTED_IDENTIFIER est toujours ON

    • méfiez-vous, il est pris en compte dans le temps d'analyse, c'est-à-dire même dans les sections de code inaccessibles

En vous conformant à ces 4 points, vous devriez être en sécurité. Si vous en violez une, un moyen d’injection SQL s’ouvre.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top