web-dev-qa-db-fra.com

Une instruction paramétrée peut-elle arrêter toute injection SQL?

Si oui, pourquoi y a-t-il encore autant d'injections SQL réussies? Juste parce que certains développeurs sont trop bêtes pour utiliser des instructions paramétrées?

67
iceagle

Les liens que j'ai postés dans mes commentaires sur la question expliquent très bien le problème. J'ai résumé mes sentiments sur la raison pour laquelle le problème persiste, ci-dessous:

  1. Ceux qui débutent n’ont peut-être aucune connaissance de l’injection SQL.

  2. Certains sont conscients de l'injection SQL, mais pensent que s'échapper est la solution (unique?). Si vous effectuez une recherche Google rapide pour php mysql query, la première page qui apparaît est la page mysql_query , sur laquelle figure un exemple montrant l'interpolation d'une entrée utilisateur échappée dans une requête. Il n'y a aucune mention (du moins pas que je puisse voir) d'utiliser des instructions préparées à la place. Comme d'autres l'ont dit, il y a tellement de didacticiels qui utilisent l'interpolation de paramètres qu'il n'est pas vraiment surprenant de savoir à quelle fréquence.

  3. Un manque de compréhension du fonctionnement des instructions paramétrées. Certains pensent que ce n'est qu'un moyen sophistiqué d'échapper aux valeurs. 

  4. D'autres connaissent les instructions paramétrées, mais ne les utilisent pas car elles ont entendu dire qu'elles sont trop lentes. J'imagine que beaucoup de gens ont entendu dire à quel point les déclarations paramétrées sont incroyablement lentes, mais n'ont en réalité effectué aucun test. Comme Bill Karwin l'a souligné dans son exposé, la différence de performance ne devrait que rarement être prise en compte lors de l'utilisation de déclarations préparées. Les avantages de prepare une fois, exécutent de nombreux, semblent souvent oubliés, de même que les améliorations apportées à la sécurité et à la maintenabilité du code.

  5. Certaines utilisent des instructions paramétrées partout, mais avec une interpolation des valeurs non vérifiées telles que les noms de tables et de colonnes, les mots-clés et les opérateurs conditionnels. Les recherches dynamiques, telles que celles qui permettent aux utilisateurs de spécifier un nombre de champs de recherche différents, des conditions de comparaison et un ordre de tri, en sont de parfaits exemples.

  6. Faux sentiment de sécurité lors de l'utilisation d'un ORM. Les ORM permettent toujours l’interpolation des parties des instructions SQL - voir 5.

  7. La programmation est un sujet vaste et complexe, la gestion de base de données est un sujet vaste et complexe, la sécurité est un sujet vaste et complexe. Développer une application de base de données sécurisée n’est pas facile - même les développeurs expérimentés peuvent être pris au dépourvu.

  8. Beaucoup de réponses sur stackoverflow n'aident pas. Lorsque les gens écrivent des questions qui utilisent le SQL dynamique et l’interpolation des paramètres, il manque souvent des réponses suggérant d’utiliser des instructions paramétrées. À quelques reprises, des gens ont réfuté ma suggestion d'utiliser des déclarations préparées, généralement à cause de la surcharge de performances perçue comme inacceptable. Je doute sérieusement que ceux qui posent la plupart de ces questions se trouvent dans une situation où les quelques millisecondes supplémentaires nécessaires pour préparer une déclaration paramétrée auront un effet catastrophique sur leur application.

56
Mike

Lorsque les articles parlent de requêtes paramétrées arrêtant les attaques SQL, ils n'expliquent pas vraiment pourquoi, mais il s'agit souvent de "C'est le cas, alors ne demandez pas pourquoi" - peut-être parce qu'ils ne se connaissent pas. Un signe certain d'un mauvais éducateur est celui qui ne peut pas admettre qu'il ne sait rien. Mais je m'éloigne du sujet… .. Quand je dis que j'ai trouvé cela totalement compréhensible d'être confondu, c'est simple. Imaginez une requête SQL dynamique

sqlQuery='SELECT * FROM custTable WHERE User=' + Username + ' AND Pass=' + password

une simple injection SQL consisterait donc simplement à entrer le nom d'utilisateur sous la forme 'OR 1 = 1 -- La requête SQL serait alors:

sqlQuery='SELECT * FROM custTable WHERE User='' OR 1=1-- ' AND PASS=' + password

Cela signifie que tous les clients dont le nom d'utilisateur est sélectionné sont vides ('') ou 1 = 1, ce qui est un booléen, ce qui équivaut à true. Ensuite, il utilise - pour commenter le reste de la requête. Donc, cela va simplement imprimer toute la table des clients, ou faire ce que vous voulez avec, si vous vous connectez, il vous connectera avec les privilèges du premier utilisateur, qui peuvent souvent être l'administrateur.

Maintenant, les requêtes paramétrées le font différemment, avec un code comme:

sqlQuery='SELECT * FROM custTable WHERE User=? AND Pass=?'

parameters.add("User", username)
parameters.add("Pass", password)

où nom d'utilisateur et mot de passe sont des variables pointant vers le nom d'utilisateur et le mot de passe associés

Maintenant, à ce stade, vous pensez peut-être que cela ne change rien du tout. Vous pouvez sûrement toujours mettre dans le champ du nom d’utilisateur quelque chose comme Nobody OR 1 = 1 '-, ce qui rend la requête efficace:

sqlQuery='SELECT * FROM custTable WHERE User=Nobody OR 1=1'-- AND Pass=?'

Et cela semblerait être un argument valable. Mais vous auriez tort.

La façon dont fonctionnent les requêtes paramétrées est que sqlQuery est envoyé en tant que requête et que la base de données sait exactement ce que fera cette requête. Ce n'est qu'alors qu'elle insérera le nom d'utilisateur et les mots de passe sous forme de valeurs. Cela signifie qu'ils ne peuvent pas effectuer la requête, car la base de données sait déjà ce que la requête va faire. Donc, dans ce cas, il faudrait rechercher un nom d'utilisateur "Personne OR 1 = 1 '-" et un mot de passe vide, ce qui devrait être faux.

Cependant, ce n'est pas une solution complète, et la validation des entrées devra toujours être effectuée, car cela n'affectera pas d'autres problèmes, tels que les attaques XSS, car vous pourriez toujours insérer du code javascript dans la base de données. Ensuite, si ceci est lu sur une page, il s'affichera en javascript normal, en fonction de la validation de la sortie. La meilleure chose à faire est donc de continuer à utiliser la validation des entrées, mais en utilisant des requêtes paramétrées ou des procédures stockées pour arrêter toute attaque SQL.

38
Josip Ivic

Eh bien bonne question ... La réponse est plus stochastique que déterministe et je vais essayer d’expliquer mon point de vue en prenant un petit exemple.

Il existe de nombreuses références sur le net qui nous suggèrent d’utiliser des paramètres dans nos requêtes ou d’utiliser des procédures stockées avec des paramètres afin d’éviter l’injection SQL (SQLi). Je vais vous montrer que les procédures stockées (par exemple) ne sont pas le bâton magique contre SQLi. La responsabilité reste toujours sur le programmeur.

Considérez la procédure stockée SQL Server suivante qui obtiendra la ligne utilisateur à partir d'une table 'Utilisateurs':

create procedure getUser
 @name varchar(20)
,@pass varchar(20)
as
declare @sql as nvarchar(512)
set @sql = 'select usrID, usrUName, usrFullName, usrRoleID '+
           'from Users '+
           'where usrUName = '''+@name+''' and usrPass = '''+@pass+''''
execute(@sql)

Vous pouvez obtenir les résultats en donnant comme paramètres le nom d'utilisateur et le mot de passe. En supposant que le mot de passe soit en texte libre (pour la simplicité de cet exemple), un appel normal serait:

DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)

EXECUTE @RC = [dbo].[getUser] 
   @name = 'admin'
  ,@pass = '!@Th1siSTheP@ssw0rd!!'
GO

Mais ici, nous avons une mauvaise technique de programmation utilisée par le programmeur dans la procédure stockée, de sorte qu'un attaquant peut exécuter ce qui suit:

DECLARE @RC int
DECLARE @name varchar(20)
DECLARE @pass varchar(20)

EXECUTE @RC = [TestDB].[dbo].[getUser] 
   @name = 'admin'
  ,@pass = 'any'' OR 1=1 --'
GO

Les paramètres ci-dessus seront transmis en tant qu'arguments à la procédure stockée et la commande SQL qui sera finalement exécutée est la suivante:

select usrID, usrUName, usrFullName, usrRoleID 
from Users 
where usrUName = 'admin' and usrPass = 'any' OR 1=1 --'

..qui obtiendra toutes les lignes des utilisateurs

Le problème ici est que même si nous suivons le principe "Créer une procédure stockée et passer les champs à rechercher en tant que paramètres", SQLi est toujours exécuté. En effet, nous copions simplement notre mauvaise pratique de programmation dans la procédure stockée. La solution au problème consiste à réécrire notre procédure stockée comme suit:

alter procedure getUser
 @name varchar(20)
,@pass varchar(20)
as
select usrID, usrUName, usrFullName, usrRoleID 
from Users 
where usrUName = @name and usrPass = @pass

Ce que j'essaie de dire, c'est que les développeurs doivent d'abord apprendre ce qu'est une attaque SQLi et comment procéder, puis protéger leur code en conséquence. Suivre aveuglément les «meilleures pratiques» n'est pas toujours la manière la plus sûre… et c'est peut-être pour cela que nous avons tant de «meilleures pratiques» - qui échouent!

9
Andreas Venieris

Oui, l'utilisation d'instructions préparées arrête toutes les injections SQL, du moins en théorie. En pratique, les instructions paramétrées peuvent ne pas être de véritables instructions préparées, par ex. PDO dans PHP les émule par défaut afin que soit ouvert à une attaque par la casse Edge .

Si vous utilisez de vraies déclarations préparées, tout est en sécurité. Eh bien, au moins tant que vous ne concaténez pas un code SQL non sécurisé dans votre requête en réaction à l'impossibilité de préparer des noms de table par exemple.

Si oui, pourquoi y a-t-il encore autant d'injections SQL réussies? Juste parce que certains développeurs sont trop bêtes pour utiliser des instructions paramétrées?

Oui, l’éducation est l’essentiel ici, et les anciennes bases du code. De nombreux tutoriels utilisent échapper et ceux-ci ne peuvent malheureusement pas être facilement supprimés du Web.

5
kelunik

J'évite les absolus dans la programmation; il y a toujours une exception. Je recommande fortement les procédures stockées et les objets de commande. La majorité de mes connaissances sont avec SQL Server, mais je joue de temps en temps avec MySql. Les procédures stockées, y compris les plans de requête en cache, présentent de nombreux avantages. Oui, cela peut être accompli avec des paramètres et SQL en ligne, mais cela ouvre plus de possibilités d'attaques par injection et ne permet pas de séparer les problèmes. Pour moi, il est également beaucoup plus facile de sécuriser une base de données car mes applications ne disposent généralement que de l'autorisation d'exécution pour lesdites procédures stockées. Sans accès direct aux tables/vues, il est beaucoup plus difficile d'injecter quoi que ce soit. Si l'utilisateur d'applications est compromis, un seul utilisateur est autorisé à exécuter exactement ce qui était prédéfini.

Mes deux centimes.

3
Derek

Je ne dirais pas "bête".

Je pense que les tutoriels sont le problème. La plupart des tutoriels SQL, des livres, peu importe ce qui explique SQL avec des valeurs intégrées, ne mentionnant aucun paramètre de liaison Les personnes qui apprennent grâce à ces tutoriels n'ont pas la chance de bien apprendre.

2
Markus Winand

Une instruction paramétrée peut-elle arrêter toute injection SQL?

Oui, tant que votre pilote de base de données offre un espace réservé pour tous les littéraux SQL possibles. La plupart des pilotes d'instructions préparés ne le sont pas. Disons que vous ne trouverez jamais d'espace réservé pour un nom de champ ou un tableau de valeurs. Ce qui incitera un développeur à adapter manuellement une requête en utilisant la concaténation et le formatage manuel. Avec le résultat prévu.

C'est pourquoi j'ai conçu mon wrapper Mysql pour PHP, qui prend en charge la plupart des littéraux pouvant être ajoutés à la requête de manière dynamique, y compris les tableaux et les identificateurs.

Si oui, pourquoi y a-t-il encore autant d'injections SQL réussies? Juste parce que certains développeurs sont trop bêtes pour utiliser des instructions paramétrées?

Comme vous pouvez le constater, en réalité, il est impossible de paramétrer toutes vos requêtes, même si vous n'êtes pas idiot. 

2

Tout d’abord, ma réponse à votre première question: Oui, pour autant que je sache, en utilisant des requêtes paramétrées, les injections SQL ne seront plus possibles. En ce qui concerne vos questions suivantes, je ne suis pas sûr et ne peux que vous donner mon opinion sur les raisons:

Je pense qu'il est plus facile d'écrire "simplement" la chaîne de requête SQL en concaténant différentes parties (peut-être même dépendant de certaines vérifications logiques) avec les valeurs à insérer . Il suffit de créer la requête et de l'exécuter . Un autre avantage est que vous pouvez imprimer (écho, sortie ou autre) la chaîne de requête SQL, puis utiliser cette chaîne pour une requête manuelle au moteur de base de données.

Lorsque vous travaillez avec des instructions préparées, vous avez toujours au moins une étape de plus: Vous devez créer votre requête (y compris les paramètres, bien sûr) Vous devez préparer la requête sur le serveurVous devez liez les paramètres aux valeurs réelles que vous souhaitez utiliser pour votre requêteVous devez exécuter la requête.

C'est un peu plus de travail (et pas si simple à programmer) en particulier pour certains emplois "rapides et sales" qui s'avèrent souvent être très longue durée ...

Meilleures salutations,

Boîte

2
TomS

Étant donné que la plupart des codes ne sont pas écrits dans un souci de sécurité, et que les gestionnaires, ayant le choix entre ajouter des fonctionnalités (en particulier quelque chose de visible pouvant être vendu) et sécurité/stabilité/fiabilité ancien. La sécurité n'est préoccupante que lorsque cela devient un problème.

2
evil otto

Pour protéger votre application contre l'injection SQL, procédez comme suit:

Étape 1. Contraindre l'entrée . Étape 2. Utiliser les paramètres avec les procédures stockées . Étape 3. Utiliser les paramètres avec le SQL dynamique.

Reportez-vous à http://msdn.Microsoft.com/en-us/library/ff648339.aspx

1
Fahad Hussain

L'injection SQL est un sous-ensemble du problème plus vaste d'injection de code, dans lequel les données et le code sont fournis sur le même canal et les données sont confondues avec le code. Les requêtes paramétrées empêchent cela de se produire en formant la requête en utilisant un contexte sur la nature des données et sur le code.

Dans certains cas spécifiques, cela ne suffit pas. Dans de nombreux SGBD, il est possible d'exécuter dynamiquement SQL avec des procédures stockées, introduisant une faille d'injection SQL au niveau du SGBD. L'appel d'une telle procédure stockée à l'aide de requêtes paramétrées n'empêchera pas l'exploitation de l'injection SQL dans la procédure. Un autre exemple peut être vu dans cet article de blog .

Plus généralement, les développeurs n'utilisent pas cette fonctionnalité correctement. Généralement, le code ressemble à ceci quand il est correctement exécuté:

db.parameterize_query("select foo from bar where baz = '?'", user_input)

Certains développeurs vont concaténer des chaînes, puis utiliser une requête paramétrée, ce qui ne crée pas la distinction donnée/code susmentionnée qui fournit les garanties de sécurité que nous recherchons:

db.parameterize_query("select foo from bar where baz = '" + user_input + "'")

L'utilisation correcte des requêtes paramétrées fournit une protection très forte, mais non impénétrable, contre les attaques par injection SQL.

1
Daniel Crowley

même si les instructions Prepared sont correctement utilisées dans le code de l'application Web, des failles d'injection SQL peuvent toujours exister si des composants de code de base de données construisent. exemple de procédure stockée vulnérable à SQL injection dans le paramètre @name:

CREATE PROCEDURE show_current_orders
(@name varchar(400) = NULL)
AS
DECLARE @sql nvarchar(4000)
SELECT @sql = ‘SELECT id_num, searchstring FROM searchorders WHERE ‘ +
‘searchstring = ‘’’ + @name + ‘’’’;
EXEC (@sql)
GO

Même si l'application transmet le nom fourni par l'utilisateur à la procédure Stockée de manière sécurisée, la procédure elle-même le concatène directement dans une requête dynamique Et est donc vulnérable.

0
Hadid Graphics