2009-12-07 6 views
9
public static bool TruncateTable(string dbAlias, string tableName) 
{ 
    string sqlStatement = string.Format("TRUNCATE TABLE {0}", tableName); 
    return ExecuteNonQuery(dbAlias, sqlStatement) > 0; 
}
+2

Qui est autorisé à appeler TruncateTable? – FrustratedWithFormsDesigner

+1

Pas du tout. Vous devez utiliser des requêtes paramétrées pour être sûr. http://www.c-sharpcorner.com/UploadFile/puranindia/ParameterizedQuerySQLInjectionAttacks08102009011903AM/ParameterizedQuerySQLInjectionAttacks.aspx –

+1

En général, je ne permettrais pas à une interface utilisateur d'appeler truncate table! Si vous avez besoin de faire cela, vous avez probablement un sérieux défaut de conception. – HLGEM

Répondre

22

La recommandation la plus courante pour lutter contre l'injection SQL consiste à utiliser un paramètre de requête SQL (plusieurs personnes sur ce thread l'ont suggéré).

Ceci est la mauvaise réponse dans ce cas. Vous ne pouvez pas utiliser un paramètre de requête SQL pour un nom de table dans une instruction DDL.

Les paramètres de requête SQL peuvent uniquement être utilisés à la place d'une valeur littérale dans une expression SQL. C'est standard dans chaque implémentation de SQL.

Ma recommandation pour la protection contre l'injection SQL lorsque vous avez un nom de table est de valider la chaîne d'entrée par rapport à une liste de noms de tables connus.

Vous pouvez obtenir une liste des noms de table valides du INFORMATION_SCHEMA:

SELECT table_name 
FROM INFORMATION_SCHEMA.Tables 
WHERE table_type = 'BASE TABLE' 
    AND table_name = @tableName 

Maintenant, vous pouvez passer votre variable d'entrée à cette requête en tant que paramètre SQL. Si la requête ne renvoie aucune ligne, vous savez que l'entrée n'est pas valide pour être utilisée en tant que table. Si la requête renvoie une ligne, elle correspond, de sorte que vous avez plus d'assurance que vous pouvez l'utiliser en toute sécurité.

Vous pouvez également valider le nom de la table par rapport à une liste de tables spécifiques que vous définissez comme acceptable pour votre application à tronquer, comme @John Buchanan suggests.

Même après avoir validé que tableName existe en tant que nom de table dans votre SGBDR, je suggère également de délimiter le nom de la table, juste au cas où vous utilisez des noms de table avec des espaces ou des caractères spéciaux. Dans Microsoft SQL Server, les délimiteurs d'identification par défaut sont entre crochets:

string sqlStatement = string.Format("TRUNCATE TABLE [{0}]", tableName); 

Maintenant, vous êtes seulement à risque d'injection SQL si tableName correspond à une vraie table, et que vous utilisez réellement entre crochets dans les noms de vos tables!

+2

Valider par connaître les entrées possibles. +1 – Greg

+0

Je pense que beaucoup de gens ont oublié que vous ne pouvez pas utiliser les requêtes paramétrées avec ce type de requête. Bonne observation. –

+1

L'OP doit également considérer la possibilité de deux tables ou plus ayant le même nom mais appartenant à des propriétaires/schémas différents. – MartW

1

Jetez un oeil à ce lien

Does this code prevent SQL injection?

Retirez les indésirables de la chaîne tableName.

Je ne pense pas que vous pouvez utiliser la requête param pour un nom de table.

1

Utilisez des requêtes paramétrées.

+3

D'un nom de table? Avez-vous des liens pour soutenir cela? –

+1

Une réponse étrangement correcte mais mauvaise. Un petit exemple basé sur le code OP serait bien meilleur. – AnthonyWJones

+0

C'est vrai, mais les gens devraient vraiment comprendre les dangers de laisser passer n'importe quelle chaîne dans une requête sans utiliser de requêtes paramétrées. Je n'ai pas eu le temps d'écrire un exemple de code, mais il devrait le savoir. –

2

Utilisez une procédure stockée. Toute bibliothèque db décent (MS Enterprise Library est ce que j'utilise) gère correctement les paramètres de chaîne d'échappement. En outre, re: requêtes paramétrés: je préfère ne pas avoir à redéployer mon application pour résoudre un problème de db. Le stockage des requêtes en tant que chaînes littérales dans votre source augmente la complexité de la maintenance.

+0

Si vous ne voulez pas redéployer, faites-le correctement en premier lieu et testez-le. –

+4

Pourquoi je n'y ai pas pensé. Pendant que j'y suis, je vais arrêter de mettre tous ces bugs stupides que j'ai codés pendant toutes ces années. Aussi, le génie, * deploy * peut signifier beaucoup de choses - comme aller de la boîte locale au serveur de dev. Interrompre d'autres développeurs en reconstruisant/redploy en dev peut être une grosse perturbation. –

+1

Il peut également être dérangeant de déployer une nouvelle révision d'une procédure stockée. –

-2

Vous pouvez utiliser SQLParameter pour transmettre la valeur tableName. Pour autant que je sache et que je sois testé, SQLParameter s'occupe de tous les contrôles de paramètres et désactive ainsi la possibilité d'injection.

-4

Si vous ne pouvez pas utiliser de requêtes paramétrées (et vous devriez le faire) ... un simple remplacement de toutes les instances de 'avec' devrait fonctionner.

string sqlStatement = string.Format("TRUNCATE TABLE {0}", tableName.Replace("'", "''")); 
+1

à peine, qu'en est-il '-' – CaffGeek

+0

vous avez raison. il y a en fait un certain nombre de choses qui doivent être remplacées (par exemple '; -,/* */xp_, etc ...) et donc ma recommandation d'utiliser une requête paramétrée (ou toute autre option valide, par ex. ORM, etc ...). tout de même, tout en limitant la longueur de la chaîne "tableName" ... cette solution fonctionnerait probablement à 100% du temps. – wgpubs

6

Pour autant que je sache, vous ne pouvez pas utiliser des requêtes paramétrées pour exécuter des instructions DDL/spécifier les noms de table, du moins pas dans Oracle ou SQL Server. Ce que je ferais, si je devais avoir une fonction TruncateTable folle, qui devait être sûre de l'injection sql serait de faire une procédure stockée qui vérifie que l'entrée est une table qui est sûre de tronquer.


-- Sql Server specific! 
CREATE TABLE TruncableTables (TableName varchar(50)) 
Insert into TruncableTables values ('MyTable') 

go 

CREATE PROCEDURE MyTrunc @tableName varchar(50) 
AS 
BEGIN 

declare @IsValidTable int 
declare @SqlString nvarchar(50) 
select @IsValidTable = Count(*) from TruncableTables where TableName = @tableName 

if @IsValidTable > 0 
begin 
select @SqlString = 'truncate table ' + @tableName 
EXECUTE sp_executesql @SqlString 
end 
END 
1

Il y a quelques autres messages qui aideront à l'injection SQL, donc je vais Upvote ceux-ci, mais une autre chose à considérer est la façon dont vous allez traiter les autorisations pour cela. Si vous accordez aux utilisateurs des rôles db + owner ou db_ddladmin afin qu'ils puissent tronquer des tables, il ne suffit pas d'éviter les attaques par injection SQL standard. Un hacker peut envoyer d'autres noms de tables qui pourraient être valides, mais que vous ne voudriez pas tronquer.

Si vous accordez des autorisations ALTER TABLE aux utilisateurs sur les tables spécifiques que vous autorisez à être tronquées, vous êtes en meilleure forme, mais c'est plus que ce que vous souhaitez autoriser dans un environnement normal.

Habituellement, TRUNCATE TABLE n'est pas utilisé lors d'une utilisation normale au quotidien. Il est utilisé pour les scénarios ETL ou lors de la maintenance de la base de données. La seule situation où je pourrais l'imaginer serait utilisée dans une application frontale serait si vous avez permis aux utilisateurs de charger une table qui est spécifique pour cet utilisateur à des fins de chargement, mais même alors j'utiliserais probablement une solution différente. Bien sûr, sans connaître les détails sur les raisons pour lesquelles vous l'utilisez, je ne peux pas dire catégoriquement que vous devriez le redessiner, mais si j'avais une demande en tant qu'administrateur de base de données, je demanderais au développeur beaucoup de choses. des questions.

3

Si vous autorisez une entrée définie par l'utilisateur à se glisser dans cette fonction via la variable nomtable, je ne pense pas que l'injection SQL soit votre seul problème.

Une meilleure option serait d'exécuter cette commande via sa propre connexion sécurisée et ne lui donner aucun droit SELECT. Tout TRUNCATE doit être exécuté est l'autorisation ALTER TABLE. Si vous êtes sur SQL 2005 vers le haut, vous pouvez également essayer d'utiliser une procédure stockée avec EXECUTE AS à l'intérieur.

0

Dans cet exemple concret, vous n'avez besoin d'une protection contre l'injection SQL que si le nom de la table provient d'une source externe.

Pourquoi auriez-vous jamais permis que cela se produise? Si vous autorisez une entité externe (utilisateur final, autre système, quoi?) à nommer une table à supprimer, pourquoi ne pas lui donner des droits d'administrateur.

Si vous créez et supprimez des tables pour fournir certaines fonctionnalités à l'utilisateur final, ne les laissez pas fournir des noms pour les objets de base de données directement. En dehors de l'injection SQL, vous rencontrerez des problèmes avec les conflits de noms, etc. Générez à la place de vrais noms de tables (par exemple DYNTABLE_00001, DYNTABLE_00002, ...) et conservez une table qui les connecte aux noms fournis par l'utilisateur.


Quelques notes sur la génération SQL dynamique pour les opérations: DDL

  • Dans la plupart des SGBDR-s vous devez utiliser SQL dynamique et insérer des noms de table sous forme de texte. Soyez très prudent.

  • Utilisez des identificateurs entre guillemets ([] dans MS SQL Server, "" dans tous les SGBDR compatibles ANSI). Cela permettra d'éviter les erreurs causées par des noms invalides.

  • Faites-le dans des procédures stockées et vérifiez si tous les objets référencés sont valides.

  • Ne faites rien d'irréversible. Par exemple. Ne pas laisser tomber les tables automatiquement. Vous pouvez les marquer comme étant abandonnées et envoyer un e-mail à votre administrateur de base de données. Elle va les laisser tomber après la sauvegarde.

  • Évitez-le si vous le pouvez. Si vous ne pouvez pas, faites ce que vous pouvez pour minimiser les droits sur les autres tables (non dynamiques) que les utilisateurs normaux auront.

3
CREATE OR REPLACE PROCEDURE truncate(ptbl_name IN VARCHAR2) IS 
    stmt VARCHAR2(100); 
BEGIN 
    stmt := 'TRUNCATE TABLE '||DBMS_ASSERT.SIMPLE_SQL_NAME(ptbl_name); 
    dbms_output.put_line('<'||stmt||'>'); 
    EXECUTE IMMEDIATE stmt; 
END;