【问题标题】:T-SQL Dynamic pivot query and SQL Injection protectionT-SQL 动态数据透视查询和 SQL 注入保护
【发布时间】:2022-01-20 17:00:33
【问题描述】:

当导入/解析平面文件和分隔数据时(我们使用SqlBulkCopy),我们最终会得到一个包含 DATA_KEY 和 DATA_VAL 列的表。 [EDIT] 忘了说这个表是.net crl 的工作表,只解析各种入站数据。要将数据从该工作表加载到生产表中,我们然后使用动态查询来 PIVOT 数据,如下所示:

DECLARE @sqlCommand varchar(MAX)
DECLARE @columnList varchar(max)

SELECT 
    @columnList = STUFF((SELECT DISTINCT',MIN(CASE DATA_KEY WHEN '''+DATA_KEY+''' THEN DATA_VAL END) AS '+ QUOTENAME(DATA_KEY) 
                         FROM PARSED_DATA
                         FOR XML PATH ('')), 1, 1, '')

SET @sqlCommand = 'SELECT ROW_NUMBER() OVER(ORDER BY KEYID,PARENT_POS) AS ROWID,  ' + @columnList + ' FROM PARSED_DATA 

这工作正常,但带来了可能的 SQL 注入问题。显然这是一个串联的字符串,因此 DATA_KEY 和 DATA_VAL 可能会被注入。当我查看 DATA_KEY 字段时,似乎注入可能起作用的唯一方法是攻击者知道这是在 CASE 语句中 - 也就是说,注入的代码必须正确地结束 CASE 语句,否则整个查询将失败。同样,如果注入没有正确结束 CASE 语句,则 DATA_VAL 字段将使整个查询失败。

我说的对吗?还是我错过了一些明显的东西?另外,我们在哪里使用 DATA_KEY 作为列名? QUOTENAME 使该值成为有效的 SQL 标识符;那里可以打针吗?

只是想确保所有的基地都被覆盖;欢迎 cmets 和建议!

【问题讨论】:

  • a table with a DATA_KEY and DATA_VAL column. 不要使用这样的表格。该提议根本没有任何好处(当然没有“灵活性”),同时几乎不可能进行查询。当值完全不相关时,索引是无用的,值验证是不可能的,缓冲效率会大大降低,因为从表中加载的 lot 数据将用于不相关的“实体”。外键是不可能的。这是错误,不是聚合列名。
  • 将 QUOTENAME() 包裹在列名周围,这将保护您至少在该特定位置免受任何可能的注入。请参阅 mssqltips.com/sqlservertip/3637/…mssqltips.com/sqlservertip/3638/… 另外,如果您在 2017 年以上,请查看 STRING_AGG,它比 FOR XML PATH 更容易查看...
  • 您也可以在盲目地将DATA_KEY放入CASE表达式之前检查是否在sys.columns中。这确实将风险降低到几乎为零。
  • 您需要在CASE 部分还需要QUOTENAME ...CASE DATA_KEY WHEN ' + QUOTENAME(DATA_KEY, '''') + ' THEN... 您还需要更改您的FOR XML 以使用.value 以取消转义XML 字符。如果您有 SQL Server 2017+,只需使用 STRING_AGG
  • 不使用额外的参数QUOTENAME(..., ''''),这将导致'FOO'dbfiddle.uk/…

标签: sql-server tsql dynamic-sql


【解决方案1】:

我可以看到一些问题:

  • 您也应该在枢轴CASE 表达式上使用QUOTENAME。使用''''作为第二个参数,得到''作为分隔符
  • FOR XML 应该使用 .value 来转义 XML 字符
  • STUFF的第三个参数是长度。为确保它始终正确,请使用 @separator 变量
  • 动态SQL应该在nvarchar(max)
  • DISTINCT 在计算字段上的性能可能会很慢,最好先在派生表/子查询中分组
DECLARE @separator nvarchar(10) = ',';

DECLARE @columnList nvarchar(max) = STUFF(
    (SELECT @separator + 'MIN(CASE DATA_KEY WHEN ' + QUOTENAME(DATA_KEY, '''') + ' THEN DATA_VAL END) AS ' + QUOTENAME(DATA_KEY)
     FROM (SELECT DISTINCT DATA_KEY FROM PARSED_DATA) pd
     FOR XML PATH (''), TYPE
).value('text()[1]','nvarchar(max)'), 1, LEN(@separator), '');

您说您正在使用它来生成INSERT 语句,因此您还应该根据sys.columns 检查列名

【讨论】:

  • 非常有用的信息。 nvarchar 实际上正在使用中,当我输入这个示例时,我无意中将varchar 卡在那里。在STUFF....value 捕获上非常好。我不认为这曾经被用于 XML(还)——不管这是很好的内务管理并且会被改变。完成内务管理后,将对所有查询进行性能测试。此外,在进入这部分代码之前,会根据架构检查列。
  • 这不是关于 XML,而是关于 XML 会转义的字符,例如 " <&
  • 是的,我不是很清楚有没有我......我应该多说一些类似“我认为这从未在 XML 字符发挥作用的地方使用过”但是是的,谢谢。
猜你喜欢
  • 2023-04-03
  • 2012-08-25
  • 2010-11-29
  • 2017-02-22
  • 1970-01-01
  • 2021-08-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多