在.NET中的动态SQL中清理表/列名称?(防止SQL注入攻击)

11 .net sql sanitization sqlconnection dynamic-sql

我正在生成一些动态SQL,并希望确保我的代码是安全的SQL注入.

为了争论,这里是一个如何生成它的最小例子:

var sql = string.Format("INSERT INTO {0} ({1}) VALUES (@value)",
    tableName, columnName);
Run Code Online (Sandbox Code Playgroud)

在上面,tableNamecolumnName,以及@value来自不受信任来源的任何内容.由于占位符的使用@value是安全的SQL注入攻击,可以忽略.(该命令通过SqlCommand执行.)

但是,tableName并且columnName 不能作为占位符绑定,因此容易受到注入攻击.由于这是一个"真正动态"的场景,因此没有白名单tableNamecolumnName可用.

问题是:

是否有一个标准的,内置的方式来检查和/或消毒tableNamecolumnName?(SqlConnection,或帮助程序类等)如果没有,在使用第三方库的情况下执行此任务的好方法是什么?

笔记:

  • 所有SQL标识符,包括模式,都应该被接受:例如[schema].[My Table].column,就像"安全"一样table1.
  • 可以清理标识符或检测无效标识符.(它不需要确保表/列在上下文中实际有效;生成的SQL可能无效,但必须是"安全的".)

更新:

刚发现这个,并认为它有点有趣:.NET4中有一个SqlFunctions.QuoteName函数(EF4?).好的,这对我来说真的没有帮助......

Jer*_*odd 23

我不确定你是否还在研究这个问题,但DbCommandBuilder课程提供了一种方法QuoteIdentifier.这样做的主要好处是它与数据库无关,并且不涉及任何RegEx混乱.

从.NET 4.5开始,您只需使用DbConnection对象即可清理表名和列名所需的一切:

DbConnection connection = GetMyConnection(); // Could be SqlConnection
DbProviderFactory factory = DbProviderFactories.GetFactory(connection);

// Sanitize the table name
DbCommandBuilder commandBuilder = factory.CreateCommandBuilder();

string tableName = "This Table Name Is Long And Bad";
string sanitizedTableName = commandBuilder.QuoteIdentifier(tableName);

IDbCommand command = connection.CreateCommand();
command.CommandText = "SELECT * FROM " + sanitizedTableName;

// Becomes 'SELECT * FROM [This Table Name Is Long And Bad]' in MS-SQL,
// 'SELECT * FROM "This Table Name Is Long And Bad"' in Oracle, etc.
Run Code Online (Sandbox Code Playgroud)

(4.5之前,您需要一些其他方式来获取您的DbProviderFactory - 可能来自应用程序配置中的数据提供程序名称或在某处进行硬编码.)


com*_*ech 4

由于您使用的是SqlConnection,因此假设这是一个SQL Server数据库.

根据该假设,您可以使用遵循MSDN中定义的SQL Server标识符规则的正则表达式来验证表和字段名称.虽然我是正则表达式的完全和完全新手,但我确实发现这个应该接近:

[\p{L}{\p{Nd}}$#_][\p{L}{\p{Nd}}@$#_]*
Run Code Online (Sandbox Code Playgroud)

但是,正则表达式不会解决SQL Server关键字,并且不能确保表和/或列实际存在(尽管您指出这不是问题).

如果这是我的应用程序,我首先要确保最终用户没有尝试通过拒绝任何包含分号(;)的请求来执行注入.

接下来,我将通过删除有效的名称分隔符(",',[,]),将表名拆分一段以查看是否已指定架构,并对INFORMATION_SCHEMA.TABLES执行查询以确定表的存在.

例如:

SELECT 1 
FROM   INFORMATION_SCHEMA.TABLES 
WHERE  TABLE_NAME = 'tablename' 
AND    TABLE_SCHEMA = 'tableschema'
Run Code Online (Sandbox Code Playgroud)

如果使用参数创建此查询,则应进一步保护自己免受注入.

最后,我将通过执行一组类似的步骤来验证每个列名称的存在,只有在确定表有效后才使用INFORMATION_SCHEMA.COLUMNS来确定列的有效性.

我可能会从SQL Server中获取此表的有效列列表,然后验证每个请求列是否在我的代码中的列表中.这样,您就可以确切地确定哪些列出错并向用户提供反馈.