public static bool TruncateTable(string dbAlias, string tableName)
{
string sqlStatement = string.Format("TRUNCATE TABLE {0}", tableName);
return ExecuteNonQuery(dbAlias, sqlStatement) > 0;
}
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注入攻击。
你可以从INFORMATION_SCHEMA
获取有效表名列表:
SELECT table_name
FROM INFORMATION_SCHEMA.Tables
WHERE table_type = 'BASE TABLE'
AND table_name = @tableName
现在你可以将输入变量作为SQL参数传递给此查询。如果查询返回零行,则知道输入不能用作表,如果返回一行,则匹配成功,因此您可以更有把握地安全使用它。
您还可以根据您定义的特定表列表验证表名是否可用于应用程序中要截断的表,就像@John Buchanan 建议的那样。
即使在验证tableName
作为RDBMS中的表名存在之后,我还建议分隔表名,以防万一您使用带有空格或特殊字符的表名。在Microsoft SQL Server中,默认标识符分隔符是方括号:
string sqlStatement = string.Format("TRUNCATE TABLE [{0}]", tableName);
tableName
匹配一个真实的表,并且你实际上在表名中使用了方括号时,才会存在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
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;
如果您允许用户通过tablename变量输入内容到此函数中,我认为SQL注入并不是您唯一的问题。
更好的选择是通过自己的安全连接运行此命令,并完全不授予SELECT权限。TRUNCATE所需的只是ALTER TABLE权限。如果您使用的是SQL 2005或更高版本,还可以尝试使用带有EXECUTE AS的存储过程。
使用存储过程。任何像我使用的 MS Enterprise Library 这样的好的数据库库都会正确处理转义字符串参数。
另外,关于参数化查询:我更喜欢不必重新部署应用程序来修复数据库问题。在源代码中将查询存储为字面字符串会增加维护复杂性。
还有一些其他的帖子可以帮助解决SQL注入问题,所以我会点赞那些帖子,但另一个需要考虑的问题是如何处理权限。如果你授予用户db+owner或db_ddladmin角色,以便他们可以截断表,那么仅仅避免标准的SQL注入攻击是不够的。黑客可以发送其他可能有效但你不想截断的表名。
如果你给用户在特定表上的ALTER TABLE权限,并允许这些表被截断,那么情况会好一些,但在正常环境中,我仍然不太喜欢允许这样做。
通常情况下,TRUNCATE TABLE不会在日常应用程序使用中使用。它用于ETL场景或数据库维护期间。唯一可能会在面向前端应用程序中使用的情况是,如果你允许用户加载一个特定用户用于加载目的的表,但即使在这种情况下,我也可能会使用不同的解决方案。
当然,如果不知道你使用它的具体原因,我不能断言你应该重新设计,但如果作为DBA,我收到这样的请求,我会问开发人员很多问题。
使用参数化查询。
在这个具体的例子中,只有当表名来自外部来源时,您才需要保护免受SQL注入攻击。
为什么要允许这种情况发生呢? 如果您允许某些外部实体(最终用户、其他系统等)命名要删除的表,为什么不直接给他们管理员权限呢?
如果您正在创建和删除表以为最终用户提供某些功能,请不要让他们直接提供数据库对象的名称。 除了SQL注入之外,您还会遇到名称冲突等问题。 相反,自己生成真正的表名(例如DYNTABLE_00001、DYNTABLE_00002等),并保留一个连接它们与用户提供的名称的表。
关于生成动态SQL进行DDL操作的一些注意事项:
在大多数RDBMS中,您必须使用动态SQL并将表名插入为文本。要特别小心。
使用带引号的标识符([]在MS SQL Server中,“”在所有符合ANSI标准的RDBMS中)。这将使避免由无效名称引起的错误更容易。
在存储过程中执行此操作,并检查所有引用的对象是否有效。
不要做任何不可逆转的操作。例如,不要自动删除表。您可以将它们标记为要删除并向您的DBA发送电子邮件。她会在备份之后删除它们。
如果可以的话,请尽量避免使用动态SQL。如果不能避免,则尽力将普通用户对其他(非动态)表的权限最小化。