88 votes

Comment l'assainissement qui échappe aux guillemets simples peut-il être mis en échec par une injection SQL dans SQL Server ?

Pour commencer, je suis bien conscient que les requêtes paramétrées sont la meilleure option, mais je demande ce qui rend vulnérable la stratégie que je présente ci-dessous. Les gens insistent sur le fait que la solution ci-dessous ne fonctionne pas, alors je cherche un exemple de pourquoi elle ne fonctionnerait pas.

Si le SQL dynamique est construit dans un code utilisant l'échappement suivant avant d'être envoyé à un serveur SQL, quel type d'injection peut vaincre cela ?

string userInput= "N'" + userInput.Replace("'", "''") + "'"

Il a été répondu à une question similaire aquí mais je ne crois pas qu'aucune des réponses soit applicable ici.

L'échappement du guillemet simple par un "\" n'est pas possible dans le serveur SQL.

Je crois Contrebande de SQL avec Unicode (souligné aquí ) serait contrecarrée par le fait que la chaîne produite est marquée comme Unicode par le N qui précède le guillemet simple. Pour autant que je sache, il n'y a pas d'autres jeux de caractères que SQL Server traduirait automatiquement en guillemets simples. Sans guillemet simple non encodé, je ne pense pas que l'injection soit possible.

Je ne crois pas Troncature de la chaîne n'est pas non plus un vecteur viable. SQL Server ne fera certainement pas la troncature puisque la taille maximale d'un fichier de type nvarchar est de 2GB selon microsoft . Une chaîne de 2 Go est irréalisable dans la plupart des situations, et impossible dans la mienne.

Injection de second ordre pourrait être possible, mais est-ce possible si :

  1. Toutes les données qui entrent dans la base de données sont nettoyées en utilisant la méthode ci-dessus.
  2. Les valeurs de la base de données ne sont jamais ajoutées au SQL dynamique (pourquoi le feriez-vous de toute façon, alors que vous pouvez simplement faire référence à la valeur de la table dans la partie statique de toute chaîne SQL dynamique).

Je ne veux pas dire que c'est mieux ou une alternative à l'utilisation de requêtes paramétrées, mais je veux savoir comment ce que j'ai décrit est vulnérable. Des idées ?

1 votes

Non. Tu es toujours sensible aux attaques dans la forme : "SELECT * FROM MyTable WHERE Field = " + userInput quand userInput es 0; DROP TABLE OhNo; .

15 votes

Cela n'a aucun sens. Dans l'exemple ci-dessus, votre entrée utilisateur serait nettoyée en N'0 ; DROP TABLE OhNo;' avant d'être exécutée.

6 votes

Ceci ne sert qu'à assainir les variables de type chaîne. Des éléments comme "int" n'ont pas besoin d'être nettoyés s'ils sont convertis en int avant d'être ajoutés à la requête. Quoi qu'il en soit, je ne demande que la désinfection des chaînes de caractères pour le moment. Aussi, il n'y a pas besoin d'être grossier ici. Si vous pouvez penser à une façon dont ce n'est pas sûr, je serais ravi de le savoir.

53voto

Rook Points 34698

Il y a quelques cas où cette fonction d'échappement échouera. Le plus évident est celui où le guillemet simple n'est pas utilisé :

string table= "\"" + table.Replace("'", "''") + "\""
string var= "`" + var.Replace("'", "''") + "`"
string index= " " + index.Replace("'", "''") + " "
string query = "select * from `"+table+"` where name=\""+var+"\" or id="+index

Dans ce cas, vous pouvez "sortir" en utilisant une double citation, un back-tick. Dans le dernier cas, il n'y a rien à "rompre", vous pouvez donc simplement écrire 1 union select password from users-- ou toute autre charge utile sql que l'attaquant désire.

La prochaine condition où cette fonction d'échappement échouera est si une sous-chaîne est prise après que la chaîne soit échappée (et que oui J'ai trouvé des vulnérabilités comme celle-ci dans la nature) :

string userPassword= userPassword.Replace("'", "''")
string userName= userInput.Replace("'", "''")
userName = substr(userName,0,10)
string query = "select * from users where name='"+userName+"' and password='"+userPassword+"'";

Dans ce cas, un nom d'utilisateur de abcdefgji' sera transformé en abcdefgji'' par la fonction d'échappement et ensuite transformé en abcdefgji' en prenant la sous-chaîne. Ceci peut être exploité en mettant la valeur du mot de passe à n'importe quelle instruction sql, dans ce cas-ci or 1=1-- serait interprété comme sql et le nom d'utilisateur serait interprété comme abcdefgji'' and password= . La requête qui en résulte est la suivante :

select * from users where name='abcdefgji'' and password=' or 1=1-- 

T-SQL et d'autres techniques avancées d'injection sql ont déjà été mentionnées. Injection SQL avancée dans les applications SQL Server est un excellent article et vous devriez le lire si ce n'est pas déjà fait.

Le dernier point concerne les attaques unicode. Cette catégorie de vulnérabilités est due au fait que la fonction d'échappement n'est pas consciente de l'encodage multi-octets, et cela peut être un problème de sécurité. utilisé par un attaquant pour "consommer" le caractère d'échappement . L'ajout d'un "N" à la chaîne de caractères n'est pas utile, car il n'affecte pas la valeur des caractères à plusieurs octets situés plus loin dans la chaîne. Cependant, ce type d'attaque est très rare car la base de données doit être configurée pour accepter les chaînes unicode GBK (et je ne suis pas sûr que MS-SQL puisse le faire).

L'injection de code de second ordre est toujours possible. Ce modèle d'attaque est créé en faisant confiance aux sources de données contrôlées par l'attaquant. L'échappement est utilisé pour représenter les caractères de contrôle comme leur littéral. Si le développeur oublie d'échapper une valeur obtenue à partir d'un fichier select et utilise ensuite cette valeur dans une autre requête, alors bam l'attaquant aura à sa disposition un caractère littéral entre guillemets.

Testez tout, ne faites confiance à rien.

3 votes

Si je suis d'accord pour dire que jouer avec la sécurité au lieu de paramétrer la requête est mauvais, si vous lisez attentivement sa question, il a noté plusieurs étapes qu'il prendrait pour "assurer" la sécurité. 1) il s'assure qu'une valeur numérique est réellement une valeur numérique, de sorte que l'injection de l'identifiant dans votre exemple échouerait 2) il pose une question sur le serveur SQL qui n'accepte que les éléments suivants ' comme délimiteur de chaîne. L'utilisation de guillemets ou de guillemets doubles n'aiderait pas l'attaquant.

0 votes

@Nikola Radosavljevic oui, mais les back-tics et le manque de citations peuvent encore être un problème selon la façon dont il a implanté ce contrôle. Il est clair que ce post est du point de vue d'un attaquant et non d'un défenseur. La défense est bien connue et inintéressante.

3 votes

Le code de la question n'est pas vulnérable aux attaques. Il s'agit d'une bonne réponse en tant que guide général de l'échappement des chaînes de caractères et elle est à la fois correcte et intéressante ; mais les scénarios énumérés ne seraient pas en mesure de compromettre la ligne de code dans la question originale. En ce qui concerne les attaques de seconde main, cela pourrait se produire si le texte a été inséré à l'origine avec un paramètre ou comme un littéral s'il contient un caractère de citation.

20voto

StrayCatDBA Points 1092

Avec quelques stipulations supplémentaires, votre approche ci-dessus n'est pas vulnérable à l'injection SQL. Le principal vecteur d'attaque à prendre en compte est la contrebande SQL. Ce type d'attaque se produit lorsque des caractères unicodes similaires sont traduits de manière inattendue (par exemple, ` devient ' ). Il existe plusieurs endroits où une pile d'applications peut être vulnérable à la contrefaçon SQL.

  • Le langage de programmation gère-t-il les chaînes unicode de manière appropriée ? Si le langage n'est pas conscient de l'unicode, il peut identifier à tort un octet dans un caractère unicode comme un guillemet simple et l'échapper.

  • La bibliothèque de la base de données cliente (par exemple ODBC, etc.) gère-t-elle les chaînes unicode de manière appropriée ? System.Data.SqlClient dans le cadre .Net le fait, mais qu'en est-il des anciennes bibliothèques de l'ère Windows 95 ? Les bibliothèques ODBC tierces existent bel et bien. Que se passe-t-il si le pilote ODBC ne prend pas en charge l'unicode dans la chaîne de requête ?

  • La BD traite-t-elle l'entrée correctement ? Les versions modernes de SQL sont immunisées en supposant que vous utilisez N'', mais qu'en est-il de SQL 6.5 ? SQL 7.0 ? Je n'ai pas connaissance de vulnérabilités particulières, mais cela n'était pas dans le collimateur des développeurs dans les années 1990.

  • Débordements de mémoire tampon ? Un autre problème est que la chaîne citée est plus longue que la chaîne originale. Dans quelle version de Sql Server la limite de 2GB pour l'entrée a été introduite ? Avant cela, quelle était la limite ? Sur les anciennes versions de SQL, que se passait-il lorsqu'une requête dépassait la limite ? Existe-t-il des limites à la longueur d'une requête du point de vue de la bibliothèque réseau ? Ou sur la longueur de la chaîne de caractères dans le langage de programmation ?

  • Y a-t-il des paramètres linguistiques qui affectent la comparaison utilisée dans la fonction Replace() ? .Net effectue toujours une comparaison binaire pour la fonction Replace(). Cela sera-t-il toujours le cas ? Que se passera-t-il si une future version de .NET permet de remplacer ce comportement au niveau du fichier app.config ? Et si nous utilisions une expression rationnelle au lieu de Replace() pour insérer un guillemet simple ? Les paramètres régionaux de l'ordinateur affectent-ils cette comparaison ? Si un changement de comportement a eu lieu, il n'est peut-être pas vulnérable à l'injection sql, mais il peut avoir modifié par inadvertance la chaîne de caractères en changeant un caractère uni-code qui ressemblait à un guillemet simple en un guillemet simple avant qu'il n'atteigne la base de données.

Donc, en supposant que vous utilisez la fonction System.String.Replace() en C# sur la version actuelle de .Net avec la bibliothèque intégrée SqlClient contre une version actuelle (2005-2012) de SQL Server, votre approche n'est pas vulnérable. Si vous commencez à changer les choses, alors aucune promesse ne peut être faite. L'approche des requêtes paramétrées est la bonne approche en termes d'efficacité, de performance et (dans certains cas) de sécurité.

AVERTISSEMENT Les commentaires ci-dessus ne constituent pas une approbation de cette technique. Il y a plusieurs autres très bonnes raisons pour lesquelles c'est une mauvaise approche pour générer du SQL. Cependant, il n'est pas question de les détailler dans le cadre de cette question.

N'UTILISEZ PAS CETTE TECHNIQUE POUR LES NOUVEAUX AMÉNAGEMENTS.

N'UTILISEZ PAS CETTE TECHNIQUE POUR LES NOUVEAUX AMÉNAGEMENTS.

N'UTILISEZ PAS CETTE TECHNIQUE POUR LES NOUVEAUX AMÉNAGEMENTS.

0 votes

Si ce n'est pas cette technique, alors que dois-je utiliser pour CREATE LOGIN dans un nouveau développement ?

8voto

Bill Karwin Points 204877

Utilisation des paramètres de requête est meilleur, plus facile et plus rapide que l'échappement des guillemets.


Dans votre commentaire, je vois que vous avez reconnu le paramétrage, mais cela mérite d'être souligné. Pourquoi vouloir utiliser l'échappement quand on peut paramétrer ?

Sur Injection SQL avancée dans les applications SQL Server recherchez le mot "replace" dans le texte et, à partir de là, lisez des exemples où des développeurs ont autorisé par inadvertance des attaques par injection SQL, même après avoir échappé à la saisie de l'utilisateur.


Il existe un cas limite où l'échappement des guillemets avec \ entraîne une vulnérabilité, car le \ devient la moitié d'un caractère multi-octet valide dans certains jeux de caractères. Mais ceci n'est pas applicable à votre cas puisque \ n'est pas le caractère d'échappement.

Comme d'autres l'ont souligné, vous pouvez également ajouter du contenu dynamique à votre SQL pour autre chose qu'un littéral de chaîne ou de date. Les identifiants de table ou de colonne sont délimités par " en SQL, ou [ ] dans Microsoft/Sybase. Les mots-clés SQL n'ont bien sûr pas de délimiteurs. Pour ces cas, je recommande liste blanche les valeurs à interpoler.

L'essentiel est que s'échapper es une défense efficace, si vous pouvez vous assurer de le faire de manière cohérente. C'est là le risque : l'un des développeurs de votre application pourrait omettre une étape et effectuer une interpolation de chaîne de manière non sécurisée.

Bien entendu, il en va de même pour d'autres méthodes, comme le paramétrage. Elles ne sont efficaces que si vous les utilisez systématiquement. Mais je trouve qu'il est plus facile et plus rapide d'utiliser les paramètres que de chercher le bon type d'échappement. Les développeurs sont plus susceptibles d'utiliser une méthode qui est pratique et qui ne les ralentit pas.

3 votes

Comme je l'indique dans la première ligne de mon message, j'en suis bien conscient. Je demande simplement s'il existe une raison spécifique pour laquelle ce que j'ai décrit n'est pas sûr.

1 votes

C'est une excellente ressource, mais les problèmes éventuels qui y sont suggérés ne sont pas vraiment un problème ici. Tout d'abord, la construction de guillemets à l'aide de la méthode "char(0x63)" est inutile, car cette déclaration sera interprétée comme une chaîne de caractères et ne sera pas exécutée. Quant à l'injection SQL de deuxième ordre, je l'ai déjà abordée dans le message original.

1 votes

"Pourquoi utiliser l'échappement quand on peut paramétrer ?" Parfois, le code hérité est très difficile à mettre à niveau pour les requêtes paramétrées.

5voto

Gumbo Points 279147

Une injection SQL se produit si les entrées fournies par l'utilisateur sont interprétées comme des commandes. Ici, une commande signifie tout ce qui n'est pas interprété comme un élément reconnu. type de données littéral.

Si vous utilisez l'entrée de l'utilisateur uniquement dans les littéraux de données, et plus précisément uniquement dans les littéraux de chaînes de caractères, l'entrée de l'utilisateur ne sera interprétée comme autre chose que des données de chaînes de caractères que si elle peut quitter le contexte du littéral de chaîne de caractères. Pour les chaînes de caractères ou les chaînes de caractères Unicode, c'est le guillemet simple qui entoure les données littérales, tandis que les guillemets simples intégrés doivent être représentés par deux guillemets simples.

Ainsi, pour quitter un contexte de littéral de chaîne de caractères, il faut fournir un seul guillemet simple (sic), car deux guillemets simples sont interprétés comme des données de littéral de chaîne de caractères et non comme le délimiteur de fin de littéral de chaîne de caractères.

Ainsi, si vous remplacez n'importe quel guillemet simple dans les données fournies par l'utilisateur par deux guillemets simples, il sera impossible pour l'utilisateur de quitter le contexte de la chaîne littérale.

1voto

George Wesley Points 69

Il n'existe probablement pas de méthode sûre à 100 % si vous effectuez une concaténation de chaînes de caractères. Ce que vous pouvez faire, c'est essayer de vérifier le type de données de chaque paramètre et si tous les paramètres passent cette validation, poursuivez l'exécution. Par exemple, si votre paramètre doit être de type int et que vous obtenez quelque chose qui ne peut pas être converti en int, il suffit de le rejeter.

Cela ne fonctionne cependant pas si vous acceptez des paramètres nvarchar.

Comme d'autres l'ont déjà souligné. Le moyen le plus sûr est d'utiliser des requêtes paramétrées.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X