Nous avons une autre discussion ici au travail sur l'utilisation de requêtes SQL paramétrées dans notre code. Nous avons deux côtés dans la discussion: moi et quelques autres qui disent que nous devrions toujours utiliser des paramètres pour se protéger contre les injections SQL et les autres gars qui ne pensent pas que c'est nécessaire. Au lieu de cela, ils veulent remplacer les apostrophes simples par deux apostrophes dans toutes les chaînes pour éviter les injections SQL. Nos bases de données exécutent toutes Sql Server 2005 ou 2008 et notre base de code fonctionne sur .NET Framework 2.0.
Laissez-moi vous donner un exemple simple en C #:
Je veux que nous utilisions ceci:
string sql = "SELECT * FROM Users WHERE Name=@name";
SqlCommand getUser = new SqlCommand(sql, connection);
getUser.Parameters.AddWithValue("@name", userName);
//... blabla - do something here, this is safe
Alors que les autres gars veulent faire ça:
string sql = "SELECT * FROM Users WHERE Name=" + SafeDBString(name);
SqlCommand getUser = new SqlCommand(sql, connection);
//... blabla - are we safe now?
Où la fonction SafeDBString est définie comme suit:
string SafeDBString(string inputValue)
{
return "'" + inputValue.Replace("'", "''") + "'";
}
Maintenant, tant que nous utilisons SafeDBString sur toutes les valeurs de chaîne dans nos requêtes, nous devrions être en sécurité. Droite?
Il y a deux raisons d'utiliser la fonction SafeDBString. Premièrement, c'est la façon dont cela a été fait depuis l'âge de pierre, et deuxièmement, il est plus facile de déboguer les instructions SQL puisque vous voyez la requête exacte qui est exécutée sur la base de données.
Donc alors. Ma question est de savoir s'il suffit vraiment d'utiliser la fonction SafeDBString pour éviter les attaques par injection SQL. J'ai essayé de trouver des exemples de code qui enfreignent cette mesure de sécurité, mais je n'en trouve aucun exemple.
Y a-t-il quelqu'un là-bas qui peut briser ça? Comment feriez-vous cela?
EDIT: Pour résumer les réponses à ce jour:
- Personne n'a encore trouvé le moyen de contourner le SafeDBString sur Sql Server 2005 ou 2008. C'est bien, je pense?
- Plusieurs réponses ont souligné que vous bénéficiez d'un gain de performances lors de l'utilisation de requêtes paramétrées. La raison en est que les plans de requête peuvent être réutilisés.
- Nous convenons également que l'utilisation de requêtes paramétrées donne un code plus lisible et plus facile à maintenir
- De plus, il est plus facile de toujours utiliser des paramètres que d'utiliser différentes versions de SafeDBString, des conversions de chaîne en nombre et des conversions de chaîne en date.
- En utilisant des paramètres, vous obtenez une conversion de type automatique, ce qui est particulièrement utile lorsque nous travaillons avec des dates ou des nombres décimaux.
- Et enfin: n'essayez pas de faire de la sécurité vous-même comme l'a écrit JulianR. Les fournisseurs de bases de données consacrent beaucoup de temps et d'argent à la sécurité. Il n'y a aucun moyen de faire mieux et aucune raison pour laquelle nous devrions essayer de faire leur travail.
Donc, même si personne n'a pu briser la sécurité simple de la fonction SafeDBString, j'ai eu beaucoup d'autres bons arguments. Merci!
la source
Réponses:
Je pense que la bonne réponse est:
N'essayez pas de faire de la sécurité vous-même . Utilisez la bibliothèque standard de confiance disponible pour ce que vous essayez de faire, plutôt que d' essayer de le faire vous-même. Quelles que soient les hypothèses que vous faites sur la sécurité, elles peuvent être incorrectes. Aussi sûre que puisse paraître votre propre approche (et elle semble au mieux fragile), il y a un risque que vous oubliez quelque chose et voulez-vous vraiment saisir cette chance en matière de sécurité?
Utilisez des paramètres.
la source
Et puis quelqu'un va et utilise "au lieu de". Les paramètres sont, IMO, la seule façon sûre d'aller.
Cela évite également de nombreux problèmes i18n avec les dates / nombres; quelle date est le 01/02/03? Combien vaut 123 456? Vos serveurs (serveur d'application et serveur de base de données) sont-ils d'accord les uns avec les autres?
Si le facteur de risque ne les convainc pas, qu'en est-il des performances? Le SGBDR peut réutiliser le plan de requête si vous utilisez des paramètres, ce qui améliore les performances. Il ne peut pas faire cela uniquement avec la chaîne.
la source
L'argument est un non-gagnant. Si vous parvenez à trouver une vulnérabilité, vos collègues changeront simplement la fonction SafeDBString pour en tenir compte, puis vous demanderont de prouver à nouveau qu'elle est dangereuse.
Étant donné que les requêtes paramétrées sont une meilleure pratique de programmation incontestée, la charge de la preuve devrait incomber à eux d'expliquer pourquoi ils n'utilisent pas une méthode à la fois plus sûre et plus performante.
Si le problème est de réécrire tout le code hérité, le compromis facile serait d'utiliser des requêtes paramétrées dans tout nouveau code et de refactoriser l'ancien code pour les utiliser lorsque vous travaillez sur ce code.
Je suppose que le véritable problème est la fierté et l'entêtement, et vous ne pouvez pas faire grand-chose de plus à ce sujet.
la source
Tout d'abord, votre exemple pour la version "Remplacer" est incorrect. Vous devez mettre des apostrophes autour du texte:
C'est donc une autre chose que les paramètres font pour vous: vous n'avez pas à vous soucier de savoir si une valeur doit être placée entre guillemets. Bien sûr, vous pouvez intégrer cela dans la fonction, mais vous devez ensuite ajouter beaucoup de complexité à la fonction: comment connaître la différence entre 'NULL' comme nul et 'NULL' comme une simple chaîne, ou entre un nombre et une chaîne qui contient juste beaucoup de chiffres. C'est juste une autre source de bogues.
Une autre chose est la performance: les plans de requête paramétrés sont souvent mieux mis en cache que les plans concaténés, ce qui économise peut-être au serveur une étape lors de l'exécution de la requête.
De plus, échapper des guillemets simples n'est pas suffisant. De nombreux produits DB autorisent d'autres méthodes pour échapper des caractères dont un attaquant pourrait tirer parti. Dans MySQL, par exemple, vous pouvez également échapper un guillemet simple avec une barre oblique inverse. Et donc la valeur de "nom" suivante ferait exploser MySQL avec juste la
SafeDBString()
fonction, car lorsque vous doublez le guillemet simple, le premier est toujours échappé par la barre oblique inverse, laissant le deuxième "actif":De plus, JulianR soulève un bon point ci-dessous: N'essayez JAMAIS de faire des travaux de sécurité vous-même. Il est si facile de se tromper dans la programmation de sécurité de manière subtile qui semble fonctionner, même avec des tests approfondis. Puis le temps passe et un an plus tard, vous découvrez que votre système a été fissuré il y a six mois et que vous ne le saviez même pas jusqu'à ce moment-là.
Fiez-vous toujours autant que possible aux bibliothèques de sécurité fournies pour votre plateforme. Ils seront rédigés par des personnes qui font du code de sécurité pour gagner leur vie, bien mieux testés que ce que vous pouvez gérer, et gérés par le fournisseur si une vulnérabilité est découverte.
la source
Alors je dirais:
1) Pourquoi essayez-vous de réimplémenter quelque chose qui est intégré? il est là, facilement disponible, facile à utiliser et déjà débogué à l'échelle mondiale. Si de futurs bogues y sont trouvés, ils seront corrigés et disponibles à tout le monde très rapidement sans que vous ayez à faire quoi que ce soit.
2) Quels processus sont en place pour garantir que vous ne manquerez un appel à SafeDBString? Le manquer à un seul endroit pourrait ouvrir toute une série de problèmes. Dans quelle mesure allez-vous regarder ces choses et considérez combien cet effort est gaspillé lorsque la bonne réponse acceptée est si facile à atteindre.
3) Dans quelle mesure êtes-vous certain d'avoir couvert tous les vecteurs d'attaque que Microsoft (l'auteur de la base de données et de la bibliothèque d'accès) connaît dans votre implémentation SafeDBString ...
4) Est-il facile de lire la structure du SQL? L'exemple utilise + concaténation, les paramètres ressemblent beaucoup à string.Format, qui est plus lisible.
En outre, il existe 2 façons de déterminer ce qui a été réellement exécuté: lancez votre propre fonction LogCommand, une fonction simple sans problèmes de sécurité , ou même regardez une trace SQL pour déterminer ce que la base de données pense réellement se passer.
Notre fonction LogCommand est simplement:
Vrai ou faux, il nous donne les informations dont nous avons besoin sans problèmes de sécurité.
la source
Avec les requêtes paramétrées, vous obtenez plus qu'une protection contre l'injection SQL. Vous bénéficiez également d'un meilleur potentiel de mise en cache du plan d'exécution. Si vous utilisez le profileur de requêtes de serveur SQL, vous pouvez toujours voir le 'SQL exact qui est exécuté sur la base de données' afin que vous ne perdiez rien non plus en termes de débogage de vos instructions SQL.
la source
J'ai utilisé les deux approches pour éviter les attaques par injection SQL et je préfère définitivement les requêtes paramétrées. Quand j'ai utilisé des requêtes concaténées, j'ai utilisé une fonction de bibliothèque pour échapper aux variables (comme mysql_real_escape_string) et je ne serais pas sûr d'avoir tout couvert dans une implémentation propriétaire (comme il semble que vous l'êtes aussi).
la source
Vous ne pouvez pas effectuer facilement de vérification de type de l'entrée utilisateur sans utiliser de paramètres.
Si vous utilisez les classes SQLCommand et SQLParameter pour effectuer vos appels DB, vous pouvez toujours voir la requête SQL en cours d'exécution. Regardez la propriété CommandText de SQLCommand.
Je suis toujours un peu suspect de l'approche roll-your-own pour empêcher l'injection SQL lorsque les requêtes paramétrées sont si faciles à utiliser. Deuxièmement, ce n'est pas parce que «cela a toujours été fait de cette façon» que c'est la bonne façon de le faire.
la source
Ce n'est sûr que si vous êtes assuré de passer une chaîne.
Que faire si vous ne passez pas une chaîne à un moment donné? Et si vous ne transmettez qu'un nombre?
Deviendrait finalement:
la source
J'utiliserais des procédures ou des fonctions stockées pour tout, donc la question ne se poserait pas.
Là où je dois mettre SQL dans le code, j'utilise des paramètres, ce qui est la seule chose qui a du sens. Rappelez aux dissidents qu'il existe des pirates informatiques plus intelligents qu'eux et mieux incités à casser le code qui tente de les déjouer. En utilisant des paramètres, ce n'est tout simplement pas possible, et ce n'est pas comme si c'était difficile.
la source
Tout à fait d’accord sur les problèmes de sécurité.
Une autre raison d'utiliser des paramètres est l'efficacité.
Les bases de données compileront toujours votre requête et la mettront en cache, puis réutiliseront la requête en cache (ce qui est évidemment plus rapide pour les requêtes suivantes). Si vous utilisez des paramètres, même si vous utilisez des paramètres différents, la base de données réutilisera votre requête mise en cache car elle correspond en fonction de la chaîne SQL avant de lier les paramètres.
Si toutefois vous ne liez pas de paramètres, la chaîne SQL change à chaque requête (qui a des paramètres différents) et elle ne correspondra jamais à ce qui se trouve dans votre cache.
la source
Pour les raisons déjà données, les paramètres sont une très bonne idée. Mais nous détestons les utiliser car créer le paramètre et attribuer son nom à une variable pour une utilisation ultérieure dans une requête est une épave de tête à triple indirection.
La classe suivante encapsule le constructeur de chaînes que vous utiliserez couramment pour créer des requêtes SQL. Il vous permet d' écrire des requêtes paramétrées sans jamais avoir à créer de paramètre , vous pouvez donc vous concentrer sur le SQL. Votre code ressemblera à ceci ...
La lisibilité du code, j'espère que vous en conviendrez, est grandement améliorée et le résultat est une requête paramétrée appropriée.
La classe ressemble à ceci ...
la source
Depuis le temps très court que j'ai eu pour enquêter sur les problèmes d'injection SQL, je peux voir que rendre une valeur `` sûre '' signifie également que vous fermez la porte à des situations où vous pourriez réellement vouloir des apostrophes dans vos données - qu'en est-il du nom de quelqu'un , par exemple O'Reilly.
Cela laisse les paramètres et les procédures stockées.
Et oui, vous devriez toujours essayer d'implémenter le code de la meilleure façon que vous connaissez maintenant - pas seulement comme cela a toujours été fait.
la source
''
comme un littéral'
, donc votre chaîne sera vue en interne comme la séquence de caractèresO'Reilly
. C'est ce que la base de données va stocker, récupérer, comparer, etc. Si vous voulez montrer à l'utilisateur ses données après que vous les ayez échappées, conservez une copie de la chaîne non échappée à côté.Voici quelques articles qui pourraient vous aider à convaincre vos collègues.
http://www.sommarskog.se/dynamic_sql.html
http://unixwiz.net/techtips/sql-injection.html
Personnellement, je préfère ne jamais permettre à un code dynamique de toucher ma base de données, exigeant que tous les contacts se fassent via SP (et non un qui utilise SQl dynamique). Cela signifie que rien d'autre que ce que j'ai donné aux utilisateurs l'autorisation de faire ne peut être fait et que les utilisateurs internes (à l'exception des très rares utilisateurs disposant d'un accès en production à des fins d'administration) ne peuvent pas accéder directement à mes tables et créer des ravages, voler des données ou commettre une fraude. Si vous exécutez une application financière, c'est la solution la plus sûre.
la source
Il peut être cassé, mais les moyens dépendent des versions / correctifs exacts, etc.
Celui qui a déjà été évoqué est le bug de débordement / troncature qui peut être exploité.
Un autre moyen futur serait de trouver des bogues similaires à d'autres bases de données - par exemple, la pile MySQL / PHP a subi un problème d'échappement car certaines séquences UTF8 pourraient être utilisées pour manipuler la fonction de remplacement - la fonction de remplacement serait amenée à introduire les caractères d'injection.
A la fin de la journée, le mécanisme de sécurité de remplacement repose sur prévu , mais pas l' intention . Étant donné que la fonctionnalité n'était pas l'objectif prévu du code, il y a de fortes chances que certaines bizarreries découvertes cassent la fonctionnalité attendue.
Si vous avez beaucoup de code hérité, la méthode replace peut être utilisée comme un palliatif pour éviter de longues réécritures et tests. Si vous écrivez un nouveau code, il n'y a aucune excuse.
la source
Utilisez toujours des requêtes paramétrées lorsque cela est possible. Parfois, même une simple entrée sans l'utilisation de caractères étranges peut déjà créer une injection SQL si elle n'est pas identifiée comme une entrée pour un champ dans la base de données.
Alors laissez simplement la base de données faire son travail d'identification de l'entrée elle-même, sans oublier qu'elle évite également beaucoup de problèmes lorsque vous devez insérer des caractères étranges qui autrement seraient échappés ou modifiés. Il peut même économiser un temps d'exécution précieux à la fin pour ne pas avoir à calculer l'entrée.
la source
Je n'ai vu aucun autre répondeur aborder ce côté du «pourquoi le faire soi-même est mauvais», mais considérez une attaque de troncature SQL .
Il existe également la fonction
QUOTENAME
T-SQL qui peut être utile si vous ne pouvez pas les convaincre d'utiliser des paramètres. Il attrape beaucoup (tous?) Des problèmes de qoute échappés.la source
2 ans plus tard , j'ai récidivé ... Toute personne qui trouve des paramètres une douleur est la bienvenue pour essayer mon VS Extension, QueryFirst . Vous éditez votre demande dans un vrai fichier .sql (Validation, Intellisense). Pour ajouter un paramètre, il vous suffit de le saisir directement dans votre SQL, en commençant par le '@'. Lorsque vous enregistrez le fichier, QueryFirst génère des classes wrapper pour vous permettre d'exécuter la requête et d'accéder aux résultats. Il recherchera le type de base de données de votre paramètre et le mappera à un type .net, que vous trouverez en entrée des méthodes Execute () générées. Ça ne pourrait pas être plus simple. Le faire de la bonne manière est radicalement plus rapide et plus facile que de le faire de toute autre manière, et créer une vulnérabilité d'injection SQL devient impossible, ou du moins perversement difficile. Il existe d'autres avantages majeurs, comme la possibilité de supprimer des colonnes dans votre base de données et de voir immédiatement les erreurs de compilation dans votre application.
avertissement légal: j'ai écrit QueryFirst
la source
Voici quelques raisons d'utiliser des requêtes paramétrées:
la source
Il y avait peu de vulnérabilités (je ne me souviens plus de quelle base de données il s'agissait) liées au dépassement de tampon de l'instruction SQL.
Ce que je veux dire, c'est que SQL-Injection est plus que simplement "échapper à la citation", et vous n'avez aucune idée de ce qui va suivre.
la source
Une autre considération importante est le suivi des données échappées et non échappées. Il y a des tonnes et des tonnes d'applications, Web et autres, qui ne semblent pas suivre correctement le moment où les données sont en brut-Unicode, & -encodées, formatées en HTML, et cetera. Il est évident qu'il deviendra difficile de savoir quelles chaînes sont
''
codées et lesquelles ne le sont pas.C'est aussi un problème lorsque vous finissez par changer le type d'une variable - peut-être que c'était un entier, mais maintenant c'est une chaîne. Maintenant vous avez un problème.
la source