如何保护此函数免受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("'", "''"));