J'ai prêché à la fois à mes collègues et ici sur SO sur la qualité de l'utilisation des paramètres dans les requêtes SQL, en particulier dans les applications .NET. Je suis même allé jusqu'à leur promettre donnant l'immunité contre les attaques par injection SQL.
Mais je commence à me demander si c'est vraiment vrai. Existe-t-il des attaques par injection SQL connues qui réussiront contre une requête paramétrée? Pouvez-vous par exemple envoyer une chaîne qui provoque un débordement de tampon sur le serveur?
Il y a bien sûr d'autres considérations à prendre en compte pour garantir la sécurité d'une application Web (comme la désinfection des entrées utilisateur et tout le reste), mais maintenant je pense aux injections SQL. Je suis particulièrement intéressé par les attaques contre MsSQL 2005 et 2008 car ce sont mes bases de données principales, mais toutes les bases de données sont intéressantes.
Edit: Pour clarifier ce que je veux dire par paramètres et requêtes paramétrées. En utilisant des paramètres, j'entends utiliser des "variables" au lieu de construire la requête SQL dans une chaîne.
Donc au lieu de faire ça:
SELECT * FROM Table WHERE Name = 'a name'
Nous faisons ceci:
SELECT * FROM Table WHERE Name = @Name
puis définissez la valeur du paramètre @Name sur l'objet de requête/commande.
Placeholders suffisent pour empêcher les injections. Vous pouvez toujours être ouvert aux débordements de tampon, mais c'est une saveur d'attaque complètement différente d'une injection SQL (le vecteur d'attaque ne serait pas la syntaxe SQL mais binaire). Puisque les paramètres passés seront tous échappés correctement, il n'y a aucun moyen pour un attaquant de passer des données qui seront traitées comme du SQL "live".
Vous ne pouvez pas utiliser de fonctions à l'intérieur des espaces réservés, et vous ne pouvez pas utiliser des espaces réservés comme noms de colonne ou de table, car ils sont échappés et cités comme des littéraux de chaîne.
Cependant, si vous utilisez paramètres dans le cadre d'une concaténation de chaînes à l'intérieur de votre requête dynamique, vous êtes toujours vulnérable à l'injection, car vos chaînes ne seront pas échappées mais seront littérales. L'utilisation d'autres types de paramètres (tels que des nombres entiers) est sûre.
Cela dit, si vous utilisez use input pour définir la valeur de quelque chose comme security_level
, alors quelqu'un pourrait simplement se faire administrateur de votre système et disposer d'un service gratuit pour tous. Mais ce n'est qu'une validation d'entrée de base et n'a rien à voir avec l'injection SQL.
Il semble y avoir une certaine confusion dans ce fil sur la définition d'une "requête paramétrée".
Étant donné l'ancienne définition, de nombreux liens montrent des attaques qui fonctionnent.
Mais la définition "normale" est la dernière. Compte tenu de cette définition, je ne connais aucune attaque par injection SQL qui fonctionnera. Cela ne veut pas dire qu'il n'y en a pas, mais je ne l'ai pas encore vu.
D'après les commentaires, je ne m'exprime pas assez clairement, alors voici un exemple qui, je l'espère, sera plus clair:
Cette approche est ouverte à l'injection SQL
exec dbo.MyStoredProc 'DodgyText'
Cette approche n'est pas ouverte à l'injection SQL
using (SqlCommand cmd = new SqlCommand("dbo.MyStoredProc", testConnection))
{
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter newParam = new SqlParameter(paramName, SqlDbType.Varchar);
newParam.Value = "DodgyText";
.....
cmd.Parameters.Add(newParam);
.....
cmd.ExecuteNonQuery();
}
Non, il existe toujours un risque d'injection SQL chaque fois que vous interpolez des données non validées dans une requête SQL.
Les paramètres de requête permettent d'éviter ce risque en séparant les valeurs littérales de la syntaxe SQL.
'SELECT * FROM mytable WHERE colname = ?'
C'est bien, mais il y a d'autres buts d'interpoler des données dans une requête SQL dynamique qui ne peut pas utiliser des paramètres de requête, car ce n'est pas une valeur SQL mais plutôt un nom de table, un nom de colonne, une expression ou une autre syntaxe.
'SELECT * FROM ' + @tablename + ' WHERE colname IN (' + @comma_list + ')'
' ORDER BY ' + @colname'
Peu importe que vous utilisiez des procédures stockées ou exécutiez des requêtes SQL dynamiques directement à partir du code d'application. Le risque est toujours là.
Le remède dans ces cas est d'employer FIEO au besoin:
Filter Input: validez que les données ressemblent à des entiers légitimes, des noms de table, des noms de colonne, etc. avant de les interpoler.
Sortie d'échappement: dans ce cas, "sortie" signifie mettre des données dans une requête SQL. Nous utilisons des fonctions pour transformer les variables utilisées comme littéraux de chaîne dans une expression SQL, afin que les guillemets et autres caractères spéciaux à l'intérieur de la chaîne soient échappés. Nous devrions également utiliser des fonctions pour transformer des variables qui seraient utilisées comme noms de table, noms de colonne, etc. Quant à d'autres syntaxes, comme l'écriture dynamique d'expressions SQL entières, c'est un problème plus complexe.
tout paramètre sql de type chaîne (varchar, nvarchar, etc.) utilisé pour construire une requête dynamique est toujours vulnérable
sinon la conversion du type de paramètre (par exemple en int, décimal, date, etc.) devrait éliminer toute tentative d'injecter sql via le paramètre
EDIT: un exemple, où le paramètre @ p1 est destiné à être un nom de table
create procedure dbo.uspBeAfraidBeVeryAfraid ( @p1 varchar(64) )
AS
SET NOCOUNT ON
declare @sql varchar(512)
set @sql = 'select * from ' + @p1
exec(@sql)
GO
Si @ p1 est sélectionné dans une liste déroulante, il s'agit d'un vecteur d'attaque sql-injection potentiel;
Si @ p1 est formulé par programme sans la capacité de l'utilisateur à intervenir, il ne s'agit pas d'un vecteur d'attaque sql-injection potentiel
Un débordement de tampon n'est pas une injection SQL.
Les requêtes paramétrées garantissent que vous êtes protégé contre l'injection SQL. Ils ne garantissent pas qu'il n'y a pas d'exploits possibles sous forme de bogues dans votre serveur SQL, mais rien ne le garantira.
Vos données ne sont pas sécurisées si vous utilisez SQL dynamique sous quelque forme que ce soit, car les autorisations doivent être au niveau de la table. Oui, vous avez limité le type et la quantité d'attaque par injection à partir de cette requête particulière, mais pas limité l'accès qu'un utilisateur peut obtenir s'il trouve un moyen d'accéder au système et vous êtes complètement à l'abri des utilisateurs internes accédant à ce qu'ils ne devraient pas afin de commettre une fraude ou de voler des informations personnelles à vendre. Le SQL dynamique de tout type est une pratique dangereuse. Si vous utilisez des procs stockés non dynamiques, vous pouvez définir des autorisations au niveau de procesdure et aucun utilisateur ne peut rien faire sauf ce qui est défini par les procs (sauf les administrateurs système bien sûr).
Il est possible qu'un proc stocké soit vulnérable à des types spéciaux d'injection SQL via un débordement/troncature, voir: Injection Enabled by Data Truncation ici:
Vous pouvez exécuter SQL dynamique comme exemple
DECLARE @SQL NVARCHAR(4000);
DECLARE @ParameterDefinition NVARCHAR(4000);
SELECT @ParameterDefinition = '@date varchar(10)'
SET @SQL='Select CAST(@date AS DATETIME) Date'
EXEC sp_executeSQL @SQL,@ParameterDefinition,@date='04/15/2011'
N'oubliez pas qu'avec les paramètres, vous pouvez facilement stocker la chaîne, ou dire le nom d'utilisateur si vous n'avez aucune politique, "); supprimer les utilisateurs de la table; -"
En soi, cela ne causera aucun préjudice, mais vous feriez mieux de savoir où et comment cette date est utilisée plus loin dans votre application (par exemple, stockée dans un cookie, récupérée plus tard pour faire d'autres choses).