如何保护此函数免受SQL注入?

public static bool TruncateTable(string dbAlias, string tableName) { string sqlStatement = string.Format("TRUNCATE TABLE {0}", tableName); return ExecuteNonQuery(dbAlias, sqlStatement) > 0; } 

打击SQL注入的最常见建议是使用SQL查询参数(此线程上有几个人建议它)。

在这种情况下,这是错误的答案。 您不能在DDL语句中为表名使用SQL查询参数。

SQL查询参数只能用于代替SQL表达式中的文字值。 这是SQL的每个实现的标准。

当您拥有表名时,我建议防止SQL注入是针对已知表名列表validation输入字符串。

您可以从INFORMATION_SCHEMA获取有效表名称列表:

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

现在,您可以将输入变量作为SQL参数传递给此查询。 如果查询未返回任何行,则表示输入无效用作表。 如果查询返回一行,则匹配,因此您可以更安全地使用它。

您还可以根据您定义的特定表格列表validation表格名称,以便您的应用程序截断,正如@John Buchanan 建议的那样 。

即使在validation了tableName作为RDBMS中的表名存在之后,我也建议分隔表名,以防您使用带有空格或特殊字符的表名。 在Microsoft SQL Server中,默认标识符分隔符是方括号:

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

现在,如果tableName与真实表匹配,您只会面临SQL注入的风险,并且您实际上在表的名称中使用方括号!

据我所知,您不能使用参数化查询来执行DDL语句/指定表名,至少不能在Oracle或Sql Server中使用。 我要做的,如果我必须有一个疯狂的TruncateTable函数,必须从sql注入安全将是一个存储过程,检查输入是一个可以安全截断的表。

 -- 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 

如果您允许用户定义的输入通过tablename变量进入此函数,我不认为SQL注入是您唯一的问题。

更好的选择是通过自己的安全连接运行此命令,并且根本不给它任何SELECT权限。 需要运行的所有TRUNCATE都是ALTER TABLE权限。 如果你在SQL 2005上,你也可以尝试在内部使用EXECUTE AS的存储过程。

 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; 

使用存储过程。 任何像样的数据库库(我使用的MS企业库)都将正确处理转义字符串参数。

另外,re:参数化查询:我更喜欢不必重新部署我的应用程序来修复数据库问题。 在源中将查询存储为文字字符串会增加维护复杂性。

使用参数化查询。

看看这个链接

此代码是否会阻止SQL注入?

从tableName字符串中删除不需要的内容。

我不认为你可以使用param查询表名。

还有一些其他post可以帮助SQL注入,所以我会提出这些,但另外要考虑的是你将如何处理这个权限。 如果您授予用户db + owner或db_ddladmin角色以便它们可以截断表,那么仅仅避免标准SQL注入攻击是不够的。 黑客可以发送其他可能有效的表名,但是您不希望将其截断。

如果你给特定表上的用户提供ALTER TABLE权限,你将被允许被截断,那么你的形状会更好,但它仍然比我在正常环境中允许的更多。

通常TRUNCATE TABLE不用于正常的日常应用程序使用。 它用于ETL场景或数据库维护期间。 我可以想象它将用于前端应用程序的唯一情况是,如果您允许用户加载特定于该用户的表以用于加载目的,但即便如此,我可能会使用不同的解决方案。

当然,在不知道你为什么使用它的具体细节的情况下,我不能断然说你应该重新设计,但如果我作为DBA得到这个请求,我会向开发人员提出很多问题。

在此具体示例中,仅当表名来自外部源时,才需要保护SQL注入。

为什么你会允许这种情况发生? 如果您允许某个外部实体(最终用户,其他系统,什么?)来命名要删除的表,为什么不给它们管理员权限。

如果要创建和删除表以为最终用户提供某些function,请不要让它们直接为数据库对象提供名称。 除了SQL注入之外,你还会遇到名称冲突等问题。而是自己生成真实的表名(例如DYNTABLE_00001,DYNTABLE_00002,…)并保留一个表,将它们连接到用户提供的名称。


关于为DDL操作生成动态SQL的一些注意事项:

  • 在大多数RDBMS中,您必须使用动态SQL并将表名作为文本插入。 要格外小心。

  • 在所有ANSI兼容的RDBMS中使用带引号的标识符(MS SQL Server中的[],“”)。 这样可以更容易避免由无效名称引起的错误。

  • 在存储过程中执行此操作并检查所有引用的对象是否有效。

  • 不要做任何不可逆转的事情。 例如,不要自动删除表。 您可以标记它们以便删除并通过电子邮件发送给您的DBA。 备份她会放弃它们。

  • 如果可以,请避免使用它。 如果不能,请尽可能减少普通用户将拥有的其他(非动态)表的权限。

您可以使用SQLParameter传入tableName值。 据我所知和测试,SQLParameter负责所有参数检查,从而禁用注入的可能性。

如果你不能使用参数化查询(你应该)…简单地替换’with’的所有实例应该有效。

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