Pour commencer, je suis bien conscient que les requêtes paramétrées sont la meilleure option, mais je me demande ce qui rend la stratégie que je présente ci-dessous vulnérable. 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 code SQL dynamique est intégré dans le code à l'aide de l'échappement suivant avant d'être envoyé à un serveur SQL, quel type d'injection peut vaincre cela?
string userInput= "N'" + userInput.Replace("'", "''") + "'"
Une question similaire a été répondue ici , mais je ne crois pas que les réponses soient applicables ici.
Il n'est pas possible d'échapper le guillemet simple avec un "\" dans SQL Server.
Je crois que la contrebande SQL avec Unicode (souligné ici ) serait contrecarrée par le fait que la chaîne produite est marquée comme Unicode par le N précédant 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 une seule citation. Sans une citation unique, je ne pense pas que l'injection soit possible.
Je ne crois pas que la troncature de chaînes soit un vecteur viable non plus. SQL Server ne fera certainement pas la troncature, car la taille maximale d'un nvarchar
est de 2 Go selon Microsoft . Une chaîne de 2 Go est irréalisable dans la plupart des situations et impossible dans la mienne.
Une injection de second ordre pourrait être possible, mais est-elle possible si:
Je ne dis pas que c'est mieux que 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?
Il y a quelques cas où cette fonction d'échappement échouera. Le plus évident est quand une seule citation n'est pas utilisée:
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 "éclater" en utilisant un guillemet double, un back-tick. Dans le dernier cas, il n'y a rien à "sortir", vous pouvez donc simplement écrire 1 union select password from users--
ou la charge utile sql souhaitée par l'attaquant.
La prochaine condition où cette fonction d'échappement échouera est si une sous-chaîne est prise après l'échappement de la chaîne (et oui J'ai trouvé des vulnérabilités comme celle-ci dans le sauvage):
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 puis reconverti en abcdefgji'
en prenant la sous-chaîne. Cela peut être exploité en définissant la valeur du mot de passe sur n'importe quelle instruction sql, dans ce cas or 1=1--
serait interprété comme sql et le nom d'utilisateur serait interprété comme abcdefgji'' and password=
. La requête résultante 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 où déjà mentionné. Injection SQL avancée dans les applications SQL Server est un excellent article et vous devriez le lire si vous ne l'avez pas déjà fait.
Le dernier problème concerne les attaques unicode. Cette classe de vulnérabilités provient du fait que la fonction d'échappement n'est pas consciente du codage multi-octets, et cela peut être tilisé par un attaquant pour "consommer" le caractère d'échappement . L'ajout d'un "N" à la chaîne n'aidera pas, car cela n'affecte pas la valeur des caractères multi-octets 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 de caractère. Si le développeur oublie d'échapper à une valeur obtenue à partir d'un select
puis utilise cette valeur dans une autre requête alors bam l'attaquant aura un caractère citation simple littérale à leur disposition.
Testez tout, ne faites confiance à rien.
Avec quelques stipulations supplémentaires, votre approche ci-dessus n'est pas vulnérable à l'injection SQL. Le principal vecteur d'attaque à considérer est la contrebande SQL. La contrebande SQL se produit lorsque des caractères Unicode similaires sont traduits de manière inattendue (par exemple, `` changer en ''). Il existe plusieurs emplacements où une pile d'applications pourrait être vulnérable à la contrebande SQL.
Le langage de programmation gère-t-il les chaînes Unicode de manière appropriée? Si le langage n'est pas compatible avec Unicode, il peut mal identifier un octet dans un caractère Unicode comme une simple citation et l'échapper.
La bibliothèque de base de données client (par exemple ODBC, etc.) gère-t-elle les chaînes Unicode de manière appropriée? System.Data.SqlClient dans le framework .Net, mais qu'en est-il des anciennes bibliothèques de l'ère Windows 95? Des bibliothèques tierces ODBC existent réellement. Que se passe-t-il si le pilote ODBC ne prend pas en charge l'unicode dans la chaîne de requête?
La base de données gère-t-elle correctement l'entrée? 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 ne suis pas au courant de vulnérabilités particulières, mais ce n'était pas sur le radar des développeurs dans les années 1990.
Buffer débordements? Une autre préoccupation est que la chaîne entre guillemets est plus longue que la chaîne d'origine. Dans quelle version de Sql Server la limite de 2 Go pour l'entrée a-t-elle é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 sur la longueur d'une requête du point de vue de la bibliothèque réseau? Ou sur la longueur de la chaîne dans le langage de programmation?
Y a-t-il des paramètres de langue qui affectent la comparaison utilisée dans la fonction Replace ()? .Net effectue toujours une comparaison binaire pour la fonction Replace (). Est-ce que ce sera toujours le cas? Que se passe-t-il si une future version de .NET prend en charge la substitution de ce comportement au niveau 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 se produisait, il pourrait ne pas être vulnérable à l'injection SQL, cependant, il pourrait avoir modifié par inadvertance la chaîne en changeant un caractère unicode qui ressemblait à un guillemet simple en un guillemet simple avant qu'il n'atteigne jamais 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 SqlClient intégrée par rapport à une version actuelle (2005-2012) de SQL Server, votre approche n'est pas vulnérable. Lorsque vous commencez à changer les choses, aucune promesse ne peut être faite. L'approche de requête paramétrée est l'approche correcte pour l'efficacité, pour les performances et (dans certains cas) pour la sécurité.
AVERTISSEMENT Les commentaires ci-dessus ne sont pas une approbation de cette technique. Il existe plusieurs autres très bonnes raisons pour lesquelles il s'agit d'une mauvaise approche pour générer du SQL. Cependant, les détailler n'entre pas dans le cadre de cette question.
N'UTILISEZ PAS CETTE TECHNIQUE POUR DE NOUVEAUX DÉVELOPPEMENTS.
N'UTILISEZ PAS CETTE TECHNIQUE POUR DE NOUVEAUX DÉVELOPPEMENTS.
N'UTILISEZ PAS CETTE TECHNIQUE POUR DE NOUVEAUX DÉVELOPPEMENTS.
tiliser les paramètres de requête est meilleur, plus facile et plus rapide que les guillemets d'échappement.
Concernant votre commentaire, je constate que vous avez reconnu le paramétrage, mais il mérite d'être souligné. Pourquoi voudriez-vous utiliser l'échappement alors que vous pouvez paramétrer?
Dans injection SQL avancée dans les applications SQL Server , recherchez le mot "remplacer" dans le texte, et à partir de là, lisez quelques exemples où les développeurs ont autorisé par inadvertance des attaques par injection SQL même après avoir échappé à l'entrée utilisateur.
Il y a un cas Edge où échapper des guillemets avec \
entraîne une vulnérabilité, car \
devient la moitié d'un caractère multi-octets valide dans certains jeux de caractères. Mais cela ne s'applique pas à votre cas puisque \
n'est pas le caractère qui s'échappe.
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 un littéral de date. Les identificateurs 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 la liste blanche les valeurs à interpoler.
En fin de compte, l'échappement est une défense efficace, si vous pouvez vous assurer de le faire de manière cohérente. C'est le risque: que l'un des développeurs de votre application puisse omettre une étape et effectuer une interpolation de chaîne en toute sécurité.
Bien sûr, il en va de même pour d'autres méthodes, comme le paramétrage. Ils ne sont efficaces que si vous les faites régulièrement. Mais je trouve qu'il est plus facile d'utiliser les paramètres plus rapidement que de trouver le bon type d'échappement. Et les développeurs sont plus susceptibles d'utiliser une méthode pratique qui ne les ralentit pas.
L'injection SQL se produit si les entrées fournies par l'utilisateur sont interprétées comme des commandes. Ici, la commande signifie tout ce qui n'est pas interprété comme un littéral type de données reconnu.
Maintenant, si vous utilisez l'entrée de l'utilisateur uniquement dans les littéraux de données, en particulier uniquement dans les littéraux de chaîne, l'entrée utilisateur ne sera interprétée comme quelque chose de différent des données de chaîne que si elle est capable de quitter le contexte de littéral de chaîne. Pour les chaînes de caractères ou les littéraux de chaîne Unicode, c'est le guillemet simple qui entoure les données littérales tandis que les guillemets simples incorporés doivent être représentés avec deux guillemets simples.
Ainsi, pour laisser un contexte littéral de chaîne, il faudrait fournir un seul guillemet simple (sic) car deux guillemets simples sont interprétés comme des données littérales de chaîne et non comme le délimiteur de fin de littéral de chaîne.
Donc, si vous remplacez un guillemet simple dans les données fournies par l'utilisateur par deux guillemets simples, il sera impossible pour l'utilisateur de quitter le contexte littéral de la chaîne.
L'injection SQL peut se produire via unicode. Si l'application Web a une URL comme celle-ci:
http: // mywebapp/widgets /? Code = ABC
qui génère du SQL comme select * à partir de widgets où Code = 'ABC'
mais un hacker entre ceci:
http: // mywebapp/widgets /? Code = ABC% CA% BC; drop widgets de table--
le SQL ressemblera à select * from widgets where Code = 'ABC'; drop table widgets-- '
et SQL Server exécutera deux instructions SQL. Un pour faire la sélection et un pour faire le drop. Votre code convertit probablement le% CA% BC encodé en URL en unicode U02BC qui est une "apostrophe de lettre modificatrice". La fonction Remplacer dans .Net ne traitera PAS cela comme une seule citation. Cependant, Microsoft SQL Server le traite comme un guillemet simple. Voici un exemple qui permettra probablement l'injection SQL:
string badValue = ((char)0x02BC).ToString();
badValue = badValue + ";delete from widgets--";
string sql = "SELECT * FROM WIDGETS WHERE ID=" + badValue.Replace("'","''");
TestTheSQL(sql);
Il n'y a probablement aucun moyen sûr à 100% si vous effectuez une concaténation de chaînes. Ce que vous pouvez faire est d'essayer de vérifier le type de données pour chaque paramètre et si tous les paramètres réussissent une telle validation, continuez 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, rejetez-le simplement.
Cela ne fonctionne pas si vous acceptez les paramètres nvarchar.
Comme d'autres l'ont déjà souligné. Le moyen le plus sûr consiste à utiliser une requête paramétrée.