Nom de table de variables utilisant SQL dynamic en C #

Je cherchais un moyen de saisir un nom de table de variable et il semble que le meilleur moyen consiste à utiliser SQL dynamic, bien que cela puisse conduire à une injection SQL. Quelqu’un peut-il démontrer comment cela se fait en C #? Par exemple, je veux que quelque chose implémente quelque chose comme ceci:

SqlCommand command= new SqlCommand("SELECT x FROM @table WHERE @column = @y", conn); 

Comme vous pouvez le constater, le nom de la table et le nom de la colonne sont des variables. J’utilisais la concaténation de chaînes auparavant, mais je souhaite éviter cela pour des raisons de sécurité. Vous ne savez pas vraiment si c’est important, mais la table et la colonne ne sont pas déterminées par les entrées de l’utilisateur, mais bien par les liens que l’utilisateur sélectionne. L’injection de code SQL n’est donc pas un problème ici?

Le meilleur moyen auquel je puisse penser est en deux parties.

  1. Vous avez une procédure stockée qui prend le nom en tant que paramètre. Ce processus cherche d’abord le nom de la table dans le catalogue ( Information_Schema.Table ) et obtient le nom approprié pour vérifier qu’il est valide.

  2. Ensuite, en utilisant cette recherche, la procédure stockée construit le SQL requirejs et non à partir du paramètre.

En utilisant cette recherche, vous protégez essentiellement les noms de table non valides transmis.

 CREATE PROCEDURE RunSQL (@table NVARCHAR(50)) AS BEGIN DECLARE @validTableName NVARCHAR(50) SELECT @validTableName = TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = @table AND SCHEMA_NAME = 'dbo' -- here you can limit or customise the schema IF @validTableName IS NULL RAISE ... raise an exception DECLARE @dynamicSql NVARCHAR(100) = REPLACE('SELECT * FROM dbo.[#TABLE#]', '#TABLE' @validTableName) EXECUTE sp_executesql .... @dynamicSql .... END 

Vous pouvez utiliser expand this pour valider, schéma, colonnes etc …

En fin de compte, vous devez créer votre propre code SQL et effectuer la protection contre l’injection. Il n’y a pas moyen de contourner cela.

Il semble que vous deviez utiliser du SQL dynamic, ce qui signifie que vous devrez concaténer le nom de la table dans une chaîne de requête et l’exécuter via la procédure stockée ‘sp_executesql’ dans TSQL.

L’utilisation de SqlCommand n’est pas un SQL dynamic. Bien que vous construisiez de manière dynamic une chaîne SQL, vous exécutez toujours une chaîne ancienne plaine.

Pour le faire en toute sécurité et projeter contre l’injection SQL, vous devez vous assurer que le nom de la table est valide et vous-même, par tous les moyens nécessaires. Heureusement, vous avez 3 bonnes options, dont une très simple et pratiquement infaillible.

Comme d’autres l’ont mentionné, vous pourriez:

  1. Vérifiez le nom par rapport à une liste blanche ou
  2. Recherchez le nom dans une table système pour voir s’il s’agit d’un nom de table réel ou
  3. Utilisez simplement la fonction QUOTENAME pour échapper aux caractères et en faire un identifiant entre guillemets.

J’avais une question similaire sur le fait de supprimer une table par son nom, et c’est à ce titre que la fonction QUOTENAME a été mentionnée: https://stackoverflow.com/a/19528324/88409

Pour utiliser le SQL dynamic, vous devez faire quelque chose comme ceci:

 ssortingng sql = "declare @query nvarchar(max) = 'SELECT * FROM ' + QUOTENAME(@tablename) + ' WHERE ' + QUOTENAME(@columnname) + ' = @cv'; " + "declare @params nvarchar(500) = N'@cv nvarchar(500)'; " + "exec sp_executesql @query, @params, @cv=@columnvalue;"; SqlCommand command = new SqlCommand( sql, conn ); command.Parameters.AddWithValue( "@tablename", tableName ); command.Parameters.AddWithValue( "@columnname", columnName ); command.Parameters.AddWithValue( "@columnvalue", columnValue ); 

Si, par hasard, la classe SqlCommand ne prend pas en charge une requête aussi complexe avec des instructions ‘declare’, vous devrez simplement déplacer la valeur de la chaîne ‘sql’ dans une procédure stockée prenant ces trois mêmes parameters, puis appelez-la par nom en définissant SqlCommand.CommandType sur StoredProcedure comme suit:

 SqlCommand command = new SqlCommand( "MyStoredProcedureName", conn ); command.CommandType = System.Data.CommandType.StoredProcedure; command.Parameters.AddWithValue( "@tablename", tableName ); command.Parameters.AddWithValue( "@columnname", columnName ); command.Parameters.AddWithValue( "@columnvalue", columnValue ); 

Le moyen le plus simple consiste simplement à utiliser la concaténation de chaînes pour insérer le nom de la table dans votre requête SQL. Cependant, vous seriez toujours ouvert à l’injection SQL tant qu’un utilisateur malveillant peut injecter une chaîne arbitraire dans votre requête. Par exemple, supposons que votre URL d’API soit http://example.org/all?table_name=customer&order=desc , et que vous extrayez simplement “nom_table” de l’URL du nom d’utilisateur.

Vous devriez empêcher les injections SQL possibles en disposant une liste blanche côté serveur statique des noms de table valides (par exemple, Ssortingng[] ValidTableNames = new Ssortingng[] { "customer", "purchase", ... } , et en veillant à ce que la valeur param paramètre une de ces valeurs avant de l’append à votre requête SQL.

Une fois, j’ai créé un site Web qui permettait à certains utilisateurs d’accéder à des tables spécifiques. Utilisation de l’appartenance ASP et de l’ID utilisateur J’ai eu une table qui a créé une relation entre l’ID UTILISATEUR lorsque le compte d’utilisateur a été créé. Le nom de la table auquel ils ont été autorisés à accéder a été atsortingbué à ce moment-là. Les noms de table ont été codés en dur et construits de manière dynamic en fonction des parameters du projet. Ainsi, une table de tblCustomersNew ou de tlbInventoryOld, etc. était liée à l’ID utilisateur.

Quand un utilisateur s’est connecté, j’ai simplement interrogé l’ID utilisateur sur la table SQL pour obtenir le nom de la table, puis je l’ai ajouté à la requête ou transmis à la procédure stockée.

Fonctionne très bien, tout se passe également derrière un compte d’utilisateur authentifié.

Edit: je voulais juste append que cela évite l’injection SQL, car les noms de vos tables sont prédéfinis dans le projet et stockés dans la firebase database. Il vous suffit de les interroger avec le compte d’utilisateur authentifié pour accéder aux tables sur lesquelles l’utilisateur dispose de droits.