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

时间:2009-12-07 18:44:23

标签: c# sql sql-injection

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

11 个答案:

答案 0 :(得分:22)

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

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

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

我建议在有表名时防止SQL注入,以根据已知表名列表验证输入字符串。

您可以从INFORMATION_SCHEMA

获取有效表名列表
SELECT table_name 
FROM INFORMATION_SCHEMA.Tables 
WHERE table_type = 'BASE TABLE'
  AND table_name = @tableName

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

您还可以根据您定义的应用程序截断的特定表格列表验证表格名称,如@John Buchanan suggests

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

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

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

答案 1 :(得分:6)

据我所知,您不能使用参数化查询来执行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

答案 2 :(得分:3)

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

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

答案 3 :(得分: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;

答案 4 :(得分:2)

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

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

答案 5 :(得分:1)

看一下这个链接

Does this code prevent SQL injection?

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

我认为您不能对表名使用param查询。

答案 6 :(得分:1)

使用参数化查询。

答案 7 :(得分:1)

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

如果您要为特定表上的用户提供ALTER TABLE权限,那么您将允许截断它们,那么您的形状会更好一些,但它仍然比我在正常环境中允许的更多。 / p>

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

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

答案 8 :(得分:0)

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

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

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


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

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

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

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

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

  • 如果可以,请避免使用它。如果你做不到,尽你所能,尽量减少对他人的权利 普通用户将拥有的(非动态)表格。

答案 9 :(得分:-2)

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

答案 10 :(得分:-4)

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

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