114 votes

Est-il possible de ne pas paramétrer une requête SQL lorsque le paramètre n'est pas une chaîne de caractères ?

En termes de Injection SQL Je comprends tout à fait la nécessité de paramétrer un système de gestion de la qualité. string paramètre ; c'est l'un des plus vieux trucs du livre. Mais quand est-il justifié de pas paramétrer un SqlCommand ? Y a-t-il des types de données considérés comme "sûrs" pour ne pas être paramétrés ?

Par exemple : Je ne me considère nulle part près de Je suis un expert en SQL, mais je ne vois aucun cas où il serait potentiellement vulnérable à l'injection SQL d'accepter un bool ou un int et le concaténer directement dans la requête.

Mon hypothèse est-elle correcte, ou cela pourrait-il laisser une énorme vulnérabilité de sécurité dans mon programme ?

Pour plus de clarté, cette question est étiquetée c# qui est un langage fortement typé ; quand je dis "paramètre", pensez à quelque chose comme public int Query(int id) .

15 votes

Si vous n'utilisez pas de paramètres, vous ne bénéficierez pas de la mise en cache des plans de requête, car il faudra créer un plan de requête distinct pour chaque nouvelle combinaison d'entrées que vous fournirez.

2 votes

Pensez-vous qu'une vérification est effectuée pour s'assurer que le nombre fourni qui est entré est validé pour s'assurer qu'il s'agit bien d'un nombre ?

0 votes

@ScottChamberlain La question porte sur la sécurité et non sur l'efficacité.

102voto

Rob Points 2095

Je pense que c'est sûr... techniquement mais c'est une mauvaise habitude à prendre. Vous voulez vraiment écrire des requêtes comme ça ?

var sqlCommand = new SqlCommand("SELECT * FROM People WHERE IsAlive = " + isAlive + 
" AND FirstName = @firstName");

sqlCommand.Parameters.AddWithValue("firstName", "Rob");

Elle vous laisse également vulnérable dans le cas où un type passe d'un nombre entier à une chaîne de caractères (pensez au numéro d'employé qui, malgré son nom, peut contenir des lettres).

Donc, nous avons changé le type de EmployeeNumber de int à string mais nous avons oublié de mettre à jour nos requêtes sql. Oups.

24 votes

Peut-on arrêter d'utiliser AddWithValue déjà ? blogs.msmvps.com/jcoehoorn/blog/2014/05/12/

5 votes

Quelle est la solution lorsque vous générez dynamiquement du code qui associe des valeurs à des paramètres ? L'article du blog n'aborde pas ce scénario. Oui, il suffit d'une seule ligne pour définir le type SQL. quand il est connu Mais si ce n'est pas le cas, vous avez un problème (ou vous devez annoter les modèles avec cette information).

1 votes

Alors vous êtes coincé avec AddWithValue à moins que vous ne disposiez d'un mappage des types de bases de données dans le cadre de la construction dynamique de la déclaration. Je suppose que vous disposez d'une liste de colonnes cibles et que, dans le cadre du dictionnaire, vous pourriez disposer des types si vous le souhaitez. Sinon, il suffit d'accepter l'impact sur les performances. En fin de compte, c'est juste une bonne information à connaître, je pense.

66voto

Joel Coehoorn Points 190579

Lorsque vous utilisez une plate-forme fortement typé sur un ordinateur que vous contrôlez (comme un serveur web), vous pouvez empêcher l'injection de code pour les requêtes avec seulement bool , DateTime ou int (et autres valeurs numériques). Ce qui est préoccupant, ce sont les problèmes de performance causés par le fait de forcer le serveur SQL à recompiler chaque requête, et de l'empêcher d'obtenir de bonnes statistiques sur les requêtes exécutées avec quelle fréquence (ce qui nuit à la gestion du cache).

Mais la partie "sur un ordinateur que vous contrôlez" est importante, car sinon un utilisateur peut modifier le comportement utilisé par le système pour générer des chaînes de caractères à partir de ces valeurs afin d'inclure du texte arbitraire.

J'aime aussi penser à long terme. Que se passe-t-il lorsque la vieille base de code fortement typé d'aujourd'hui est portée par traduction automatique vers le nouveau langage dynamique, et que vous perdez soudainement la vérification de type, mais que vous n'avez pas encore tous les bons tests unitaires pour le code dynamique ?

Vraiment, il n'y a aucune raison de ne pas utiliser les paramètres de requête pour ces valeurs. C'est le bonne façon pour y parvenir. Allez-y et codez en dur les valeurs dans la chaîne sql lorsqu'il s'agit vraiment de constantes, mais sinon, pourquoi ne pas simplement utiliser un paramètre ? Ce n'est pas comme si c'était difficile.

En fin de compte, je n'appellerais pas cela un bogue en soi, mais j'appellerais ça une odeur Les odeurs : quelque chose qui n'est pas un bogue en soi, mais qui est une forte indication que des bogues sont à proximité, ou le seront éventuellement. Un bon code évite de laisser des odeurs, et tout bon outil d'analyse statique le signalera.

J'ajouterai que ce n'est pas, malheureusement, le genre d'argument que vous pouvez gagner directement. Il semble qu'il s'agisse d'une situation dans laquelle il ne suffit plus d'avoir "raison", et le fait de marcher sur les plates-bandes de vos collègues pour résoudre ce problème par vous-même n'est pas de nature à favoriser une bonne dynamique d'équipe ; cela pourrait finalement nuire plus qu'aider. Une meilleure approche dans ce cas pourrait être de promouvoir l'utilisation d'un outil d'analyse statique. Cela donnerait une légitimité et une crédibilité aux efforts visant à revenir en arrière et à corriger le code existant.

1 votes

Ce n'est certainement pas un défi de le faire de manière paramétrée. Ma question s'est posée parce qu'un collègue a écrit un tas de requêtes concaténant des valeurs entières, et je me demandais si c'était une perte de temps de les corriger toutes.

0 votes

Ne le faites pas vous-même... du moins pas tout de suite. Déposez des bugs contre eux. Vous avez un système de suivi des bugs, non ?

2 votes

Je pense que ma question se résume à la question "Est-ce un bug".

54voto

Maciek Points 626

Dans certains cas, il est possible d'effectuer une attaque par injection SQL avec des variables non paramétrées (concaténées) autres que des valeurs de type chaîne - voir cet article de Jon : http://codeblog.jonskeet.uk/2014/08/08/the-bobbytables-culture/ .

Le fait est que lorsque ToString est appelé, un fournisseur de culture personnalisé peut transformer un paramètre non linéaire en sa représentation linéaire, ce qui permet d'injecter du SQL dans la requête.

15 votes

Je pense que c'est le seul post qui répond à la question, qui est essentiellement "Comment l'injection peut-elle même être possible avec int s ?"

4 votes

Cependant, si vous êtes en mesure d'injecter du code personnalisé, tel que des pièges CultureInfo difficile de savoir pourquoi vous auriez besoin d'une injection SQL de toute façon.

0 votes

Plus 1, la seule réponse qui répond réellement la question

23voto

Reza Aghaei Points 4295
"SELECT * FROM Table1 WHERE Id=" + intVariable.ToString()

Sécurité

C'est bon.
Les attaquants ne peuvent pas injecter quoi que ce soit dans votre variable int typée.

Performance

Pas OK.

Il est préférable d'utiliser des paramètres, afin que la requête soit compilée une fois et mise en cache pour la prochaine utilisation. La fois suivante, même avec des valeurs de paramètres différentes, la requête est mise en cache et n'a pas besoin d'être compilée dans le serveur de base de données.

Style de codage

Mauvaise pratique.

  • Les paramètres sont plus lisibles
  • Peut-être que cela vous habitue à des requêtes sans paramètres, alors peut-être que vous avez fait une erreur une fois et utilisé une valeur de chaîne de cette façon et alors vous devriez probablement dire au revoir à vos données. Mauvaise habitude !

    "SELECT * FROM Product WHERE Id=" + TextBox1.Text


Bien que ce ne soit pas votre question, mais peut-être utile pour les futurs lecteurs :

Sécurité

Un désastre !

Même lorsque le Id est un nombre entier, votre requête peut faire l'objet d'une injection SQL. Supposons que vous ayez une requête dans votre application "SELECT * FROM Table1 WHERE Id=" + TextBox1.Text . Un attaquant peut insérer dans la zone de texte 1; DELETE Table1 et la requête sera :

SELECT * FROM Table1 WHERE Id=1; DELETE Table1

Si vous ne voulez pas utiliser une requête paramétrée ici, vous devez utiliser des valeurs typées :

string.Format("SELECT * FROM Table1 WHERE Id={0}", int.Parse(TextBox1.Text))

Votre question

Ma question s'est posée parce qu'un collègue de travail a écrit un tas de requêtes de requêtes concaténant des valeurs entières, et je me demandais si c'était une de perdre mon temps en les corrigeant toutes.

Je pense que changer ces codes n'est pas une perte de temps. En effet, le changement est recommandé !

Si votre collègue utilise des variables int, il n'y a pas de risque de sécurité, mais je pense que changer ces codes n'est pas une perte de temps et que changer ces codes est même recommandé. Cela rend le code plus lisible, plus facile à maintenir, et rend l'exécution plus rapide.

0 votes

Même la première option n'est pas entièrement satisfaisante pour la sécurité. Le comportement de .ToString() est déterminé par un élément de configuration du système d'exploitation qui est facile à modifier pour inclure un texte arbitraire.

0 votes

@JoelCoehoorn - si l'attaquant a accès à la configuration du système d'exploitation, n'a-t-il pas déjà pénétré votre sécurité ? [Bien que la meilleure sécurité soit la sécurité redondante, je suis d'accord qu'il n'y a aucune raison de coder d'une manière qui présente ce risque.]

1 votes

@ToolmakerSteve Pour certaines applications, cela peut être déterminé par l'ordinateur client de l'utilisateur final.

18voto

Il y a en fait deux questions en une. Et la question du titre a très peu à voir avec les préoccupations exprimées par le PO dans les commentaires qui suivent.

Bien que je réalise que pour le PO, c'est son cas particulier qui compte, pour les lecteurs venant de Google, il est important de répondre à une question plus générale, qui peut être formulée comme suit : "la concaténation est-elle aussi sûre que les instructions préparées si je m'assure que chaque littéral que je concatène est sûr ? Je voudrais donc me concentrer sur cette dernière question. Et la réponse est

Définitivement NON.

L'explication n'est pas aussi directe que la plupart des lecteurs le souhaiteraient, mais je vais faire de mon mieux.

J'ai réfléchi à la question pendant un certain temps, ce qui a donné lieu à la article (bien que basé sur l'environnement PHP) où j'ai essayé de tout résumer. Il m'est apparu que la question de la protection contre l'injection SQL est souvent éludée vers des sujets connexes mais plus étroits, comme l'échappement des chaînes de caractères, la classification des types et autres. Bien que certaines de ces mesures puissent être considérées comme sûres lorsqu'elles sont prises isolément, il n'y a pas de système, ni de règle simple à suivre. Il s'agit donc d'un terrain très glissant, qui demande trop d'attention et d'expérience de la part du développeur.

La question de l'injection SQL ne peut être simplifiée à une question de syntaxe particulière. Elle est plus vaste que ce que le développeur moyen avait l'habitude de penser. C'est un méthodologie La question se pose également. Il ne s'agit pas seulement de savoir "quel formatage particulier nous devons appliquer", mais aussi " Comment il faut le faire" également.

(De ce point de vue, l'article de Jon Skeet cité dans l'autre réponse fait plutôt du mal que du bien, car il s'agit à nouveau de pinailler sur un cas limite, en se concentrant sur une question de syntaxe particulière et en omettant d'aborder le problème dans son ensemble).

Lorsque vous essayez d'aborder la question de la protection non pas comme un tout, mais comme un ensemble de questions syntaxiques différentes, vous êtes confronté à une multitude de problèmes.

  • la liste des choix de formatage possibles est vraiment énorme. Cela signifie que l'on peut facilement en négliger certains. Ou les confondre (en utilisant chaîne de caractères qui s'échappent pour identifiant par exemple).
  • La concaténation signifie que toutes les mesures de protection doivent être prises par le programmeur, et non par le programme. Ce seul problème entraîne plusieurs conséquences :
    • une telle mise en forme est manuelle. Manuel signifie extrêmement sujettes à erreur. On peut tout simplement oublier de faire une demande.
    • De plus, la tentation est grande de transférer les procédures de formatage dans une fonction centralisée, ce qui perturbe encore plus les choses et gâche des données qui ne seront pas versées dans la base de données.
  • lorsque plus d'un développeur est impliqué, les problèmes sont multipliés par un facteur de dix.
  • lorsque la concaténation est utilisée, on ne peut pas reconnaître une requête potentiellement dangereuse au premier coup d'œil : elles tous potentiellement dangereux !

Contrairement à cette pagaille, les déclarations préparées sont en effet le Saint Graal :

  • elle peut être exprimée sous la forme d'une règle simple et facile à suivre.
  • il s'agit d'une mesure essentiellement indétachable, ce qui signifie que le développeur ne peut pas intervenir et, volontairement ou non, gâcher le processus.
  • la protection contre l'injection n'est vraiment qu'une effet secondaire des déclarations préparées, dont le but réel est de produire une déclaration syntaxiquement correcte. Et un énoncé syntaxiquement correct est 100% résistant à l'injection. Pourtant, nous avons besoin que notre syntaxe soit correcte malgré toute possibilité d'injection.
  • s'il est utilisé dans son intégralité, il protège l'application quelle que soit l'expérience du développeur. Disons qu'il existe une chose appelée injection de second ordre . Et un délire très fort qui dit "afin de protéger", Échapper à toutes les entrées fournies par l'utilisateur ". Combinés ensemble, ils mènent à l'injection, si un développeur prend la liberté de décider, ce qui doit être protégé et ce qui ne doit pas l'être.

(En réfléchissant plus avant, j'ai découvert que l'ensemble actuel de placeholders n'est pas suffisant pour les besoins de la vie réelle et doit être étendu, à la fois pour les structures de données complexes, comme les tableaux, et même les mots-clés ou identifiants SQL, qui doivent parfois être ajoutés à la requête de manière dynamique aussi, mais un développeur est laissé sans armes pour un tel cas, et obligé de se rabattre sur la concaténation de chaînes de caractères, mais c'est une autre question).

Il est intéressant de noter que la controverse de cette question est provoquée par la nature très controversée de Stack Overflow. L'idée du site est de faire usage de les questions particulières des utilisateurs qui les posent directement pour atteindre l'objectif de disposer d'une base de données de des réponses d'ordre général adaptées aux utilisateurs qui viennent de la recherche . L'idée n'est pas mauvaise en soi mais elle échoue dans une situation comme celle-ci : lorsqu'un utilisateur demande à un agent de l'État de lui fournir des informations sur l'état d'avancement d'un projet. question très étroite notamment pour obtenir un argument dans un conflit avec un collègue (ou pour décider s'il vaut la peine de remanier le code). Pendant que la plupart des participants expérimentés essaient d'écrire une réponse, en gardant à l'esprit les points suivants la mission de Stack Overflow dans son ensemble, en faisant en sorte que leur réponse soit bonne pour le plus grand nombre de lecteurs possible, et pas seulement pour le PO.

10 votes

Ne pas répondre à la question

0 votes

La plupart des bases de données détectent les requêtes paramétrées déjà utilisées par l'égalité des chaînes de caractères SQL, donc l'ancienne méthode de préparation et d'utilisation des handles me semble dépassée. Ces handles ne peuvent être utilisés que dans un certain périmètre et nécessitent un codage pour garder la trace du handle. Les requêtes paramétrées devraient, à mon avis, être utilisées directement, de sorte que les plans de requête puissent être réutilisés sans suivi de handle et même entre différentes applications.

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