108 votes

Éviter l'injection SQL sans paramètres

Nous avons une autre discussion ici au travail sur l'utilisation de requêtes sql paramétrées dans notre code. Nous avons deux camps dans la discussion : Moi et quelques autres qui disons 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 de caractères pour éviter les injections sql. Nos bases de données fonctionnent toutes sous Sql Server 2005 ou 2008 et notre code fonctionne sous .NET framework 2.0.

Laissez-moi vous donner un exemple simple en C# :

Je veux qu'on utilise ça :

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é. N'est-ce pas ?

Il y a deux raisons d'utiliser la fonction SafeDBString. Premièrement, c'est la façon de faire 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.

Ainsi donc. 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 enfreint cette mesure de sécurité, mais je n'en trouve aucun.

Y a-t-il quelqu'un qui puisse le casser ? Comment le ferais-tu ?

EDIT : Pour résumer les réponses jusqu'à présent :

  • Personne n'a encore trouvé un moyen de contourner le SafeDBString sur Sql Server 2005 ou 2008. C'est bien, je pense ?
  • Plusieurs réponses ont souligné que vous obtenez un gain de performance en utilisant des requêtes paramétrées. La raison en est que les plans de requête peuvent être réutilisés.
  • Nous sommes également d'accord pour dire que l'utilisation de requêtes paramétrées donne un code plus lisible et plus facile à maintenir.
  • En outre, il est plus facile de toujours utiliser des paramètres que d'utiliser différentes versions de SafeDBString, des conversions de chaînes de caractères en nombres et des conversions de chaînes de caractères en dates.
  • En utilisant les paramètres, vous obtenez une conversion automatique des types, ce qui est particulièrement utile lorsque nous travaillons avec des dates ou des nombres décimaux.
  • Et enfin : N'essayez pas de faire 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é. Nous ne pouvons pas faire mieux et nous n'avons aucune raison d'essayer de faire leur travail.

Ainsi, alors que personne n'a été en mesure de briser la simple sécurité de la fonction SafeDBString, j'ai obtenu beaucoup d'autres bons arguments. Merci !

82voto

JulianR Points 7257

Je pense que la bonne réponse est :

N'essayez pas de faire la sécurité vous-même . Utilisez n'importe quelle bibliothèque de confiance, standard de l'industrie, disponible pour ce que vous essayez de faire, au lieu de en essayant 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 votre propre approche puisse paraître (et elle semble au mieux bancale), il y a un risque que vous négligiez quelque chose et voulez-vous vraiment prendre ce risque lorsqu'il s'agit de sécurité ?

Utilisez les paramètres.

72voto

Marc Gravell Points 482669

Et puis quelqu'un utilise " au lieu de '. Les paramètres sont, selon moi, la seule façon sûre de procéder.

Cela évite également de nombreux problèmes d'interprétation des dates et des nombres ; quelle est la date du 01/02/03 ? Combien vaut 123 456 ? Vos serveurs (serveur d'application et serveur de base de données) sont-ils compatibles entre eux ?

Si le facteur risque n'est pas convaincant pour eux, 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 le faire avec une simple chaîne de caractères.

27voto

L'argument est sans appel. Si vous parvenez à trouver une vulnérabilité, vos collègues modifieront simplement la fonction SafeDBString pour en tenir compte et vous demanderont ensuite de prouver une nouvelle fois qu'elle n'est pas sûre.

Étant donné que les requêtes paramétrées sont une meilleure pratique de programmation incontestée, la charge de la preuve devrait leur incomber 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 existant, le compromis le plus simple serait d'utiliser des requêtes paramétrées dans tout le nouveau code, et de remanier l'ancien code pour les utiliser lorsqu'on travaille sur ce code.

À mon avis, le problème réel est la fierté et l'entêtement, et il n'y a pas grand-chose de plus que vous puissiez faire à ce sujet.

19voto

Joel Coehoorn Points 190579

Tout d'abord, votre échantillon pour la version "Replace" est erroné. Vous devez mettre des apostrophes autour du texte :

string sql = "SELECT * FROM Users WHERE Name='" + SafeDBString(name) & "'";
SqlCommand getUser = new SqlCommand(sql, connection);

C'est donc une autre chose que les paramètres font pour vous : vous n'avez pas besoin de vous soucier de savoir si une valeur doit être placée entre guillemets ou non. Bien sûr, vous pourriez l'intégrer à la fonction, mais vous devriez alors ajouter beaucoup de complexité à la fonction : comment faire la différence entre 'NULL' en tant que null et 'NULL' en tant que simple chaîne de caractères, ou entre un nombre et une chaîne de caractères qui contient juste beaucoup de chiffres. C'est une source supplémentaire de bogues.

Un autre aspect concerne les performances : les plans de requête paramétrés sont souvent mieux mis en cache que les plans concaténés, ce qui permet peut-être au serveur de gagner une étape lors de l'exécution de la requête.

En outre, l'échappement des guillemets simples n'est pas suffisant. De nombreux produits DB permettent d'utiliser d'autres méthodes d'échappement 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. Ainsi, la valeur "name" suivante ferait exploser MySQL avec seulement le caractère SafeDBString() car lorsque vous doublez le guillemet simple, le premier est toujours échappé par la barre oblique inverse, laissant le second "actif" :

x\' OR 1=1;--


De plus, JulianR soulève un bon point ci-dessous : JAMAIS essayez de faire le travail de sécurité vous-même. Il est si facile de se tromper dans la programmation de la sécurité de façon subtile que apparaître pour 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é piraté il y a six mois et que vous ne le saviez même pas jusqu'à ce moment-là.

Faites toujours confiance, autant que possible, aux bibliothèques de sécurité fournies pour votre plate-forme. Elles seront écrites par des personnes qui gagnent leur vie en codant la sécurité, elles seront bien mieux testées que ce que vous pouvez gérer et elles seront réparées par le fournisseur si une vulnérabilité est découverte.

10voto

Jim T Points 7998

Donc je dirais :

1) Pourquoi essayez-vous de réimplémenter quelque chose qui est déjà intégré ? Il est là, facilement disponible, facile à utiliser et déjà débogué à l'échelle mondiale. Si des bogues sont découverts à l'avenir, ils seront corrigés et mis à la disposition de tous très rapidement sans que vous ayez à faire quoi que ce soit.

2) Quels sont les processus mis en place pour garantie que vous jamais manquer un appel à SafeDBString ? L'oublier à un seul endroit peut entraîner une multitude de problèmes. Combien de fois allez-vous regarder ces choses, et considérer combien cet effort est gaspillé quand la réponse correcte acceptée est si facile à atteindre.

3) Êtes-vous certain d'avoir couvert tous les vecteurs d'attaque connus de Microsoft (l'auteur de la base de données et de la bibliothèque d'accès) dans votre implémentation de SafeDBString ?

4) Est-il facile de lire la structure du sql ? L'exemple utilise + concaténation, les paramètres sont très proches de string.Format, ce qui est plus lisible.

De plus, il existe deux façons de déterminer ce qui a été réellement exécuté : créer sa propre fonction LogCommand, une simple fonction avec pas de problèmes de sécurité ou même regarder une trace sql pour comprendre ce que la base de données pense qu'il se passe réellement.

Notre fonction LogCommand est simple :

    string LogCommand(SqlCommand cmd)
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine(cmd.CommandText);
        foreach (SqlParameter param in cmd.Parameters)
        {
            sb.Append(param.ToString());
            sb.Append(" = \"");
            sb.Append(param.Value.ToString());
            sb.AppendLine("\"");
        }
        return sb.ToString();
    }

À tort ou à raison, elle nous donne les informations dont nous avons besoin sans problème de sécurité.

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