SQL Server - 动态数据透视表 - SQL 注入
Posted
技术标签:
【中文标题】SQL Server - 动态数据透视表 - SQL 注入【英文标题】:SQL Server - Dynamic PIVOT Table - SQL Injection 【发布时间】:2010-11-29 04:36:03 【问题描述】:很抱歉这个问题太长了,但这包含了我用来测试场景的所有 SQL,希望能清楚地说明我在做什么。
我正在构建一些动态 SQL 以在 SQL Server 2005 中生成 PIVOT 表。
以下是执行此操作的代码。通过各种选择显示原始数据,使用 GROUP BY 的值和我想要的 PIVOT 中的值。
BEGIN TRAN
--Create the table
CREATE TABLE #PivotTest
(
ColumnA nvarchar(500),
ColumnB nvarchar(500),
ColumnC int
)
--Populate the data
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('A', 'X', 1)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('A', 'Y', 2)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('A', 'Z', 3)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('A', 'X', 4)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('A', 'Y', 5)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('B', 'Z', 6)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('B', 'X', 7)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('B', 'Y', 8)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('B', 'Z', 9)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('C', 'X', 10)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('C', 'Y', 11)
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('C', 'Z', 12)
--The data
SELECT * FROM #PivotTest
--Group BY
SELECT
ColumnA,
ColumnB,
SUM(ColumnC)
FROM
#PivotTest
GROUP BY
ColumnA,
ColumnB
--Manual PIVOT
SELECT
*
FROM
(
SELECT
ColumnA,
ColumnB,
ColumnC
FROM
#PivotTest
) DATA
PIVOT
(
SUM(DATA.ColumnC)
FOR
ColumnB
IN
(
[X],[Y],[Z]
)
) PVT
--Dynamic PIVOT
DECLARE @columns nvarchar(max)
SELECT
@columns =
STUFF
(
(
SELECT DISTINCT
', [' + ColumnB + ']'
FROM
#PivotTest
FOR XML PATH('')
), 1, 1, ''
)
EXEC
('
SELECT
*
FROM
(
SELECT
ColumnA,
ColumnB,
ColumnC
FROM
#PivotTest
) DATA
PIVOT
(
SUM(DATA.ColumnC)
FOR
ColumnB
IN
(
' + @columns + '
)
) PVT
')
--The data again
SELECT * FROM #PivotTest
ROLLBACK
每当我生成任何动态 SQL 时,我总是意识到 SQL 注入攻击。因此,我在其他 INSERT 语句中添加了以下行。
INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('A', 'FOO])) PVT; DROP TABLE #PivotTest;SELECT ((GETDATE()--', 1)
当我现在运行 SQL 时,很明显,EXEC 部分删除了 #PivotTest 表,从而使最后一个 SELECT 失败。
所以我的问题是,有没有人知道在不冒 SQL 注入攻击风险的情况下执行动态 PIVOT 的方法?
【问题讨论】:
【参考方案1】:DECLARE @PvtColumns varchar(max)
SET @PvtColumns = STUFF((SELECT ',MAX(CASE WHEN Seq = ' + CAST(Seq AS varchar(10)) + ' THEN gr_hdr_grno END) AS grNo_' + CAST(Seq AS varchar(10))
+',MAX(CASE WHEN Seq = ' + CAST(Seq AS varchar(10)) + ' THEN gr_hdr_docvalue END) AS gramt_' + CAST(Seq AS varchar(10))
+',MAX(CASE WHEN Seq = ' + CAST(Seq AS varchar(10)) + ' THEN gr_tcd_amt END) AS grtcd_' + CAST(Seq AS varchar(10))
+',MAX(CASE WHEN Seq = ' + CAST(Seq AS varchar(10)) + ' THEN document_no END) AS sobi_' + CAST(Seq AS varchar(10))
+',MAX(CASE WHEN Seq = ' + CAST(Seq AS varchar(10)) + ' THEN sobiamount END) AS samt_' + CAST(Seq AS varchar(10))
+',MAX(CASE WHEN Seq = ' + CAST(Seq AS varchar(10)) + ' THEN sobivat END) AS svat_' + CAST(Seq AS varchar(10))
FROM (SELECT DISTINCT Seq FROM (SELECT ROW_NUMBER() OVER (PARTITION BY pomas_pono ORDER BY pomas_pono) AS Seq
FROM po_grn_vat_supp)t)r
ORDER BY Seq
FOR XML PATH('')),1,1,'')
DECLARE @SQL varchar(max) = 'SELECT supp_spmn_supcode,supp_spmn_supname,supp_bu_language,vatregno,pomas_pono,pomas_pobasicvalue,pomas_tcdtotalrate,' + @PvtColumns + '
FROM (SELECT ROW_NUMBER() OVER (PARTITION BY pomas_pono ORDER BY pomas_pono) AS Seq,*
FROM po_grn_vat_supp)t GROUP BY supp_spmn_supcode,supp_spmn_supname,supp_bu_language,vatregno,pomas_pono,pomas_pobasicvalue,pomas_tcdtotalrate'
EXEC (@SQL)
【讨论】:
【参考方案2】:一点重构...
CREATE PROCEDURE ExecutePivot (
@TableName sysname,
@GroupingColumnName sysname,
@AggregateExpression VARCHAR(256),
@SelectExpression VARCHAR(256),
@TotalColumnName VARCHAR(256) = 'Total',
@DefaultNullValue VARCHAR(256) = NULL,
@IsExec BIT = 1)
AS
BEGIN
DECLARE @DistinctGroupedColumnsQuery VARCHAR(MAX);
SELECT @DistinctGroupedColumnsQuery = CONCAT('SELECT DISTINCT ',@GroupingColumnName,' FROM ',@TableName,';');
DECLARE @DistinctGroupedColumnsResult TABLE ( [row] VARCHAR(MAX) );
INSERT INTO @DistinctGroupedColumnsResult EXEC(@DistinctGroupedColumnsQuery);
DECLARE @GroupedColumns VARCHAR(MAX);
SELECT @GroupedColumns = STUFF ( ( SELECT DISTINCT CONCAT(', ',QUOTENAME([row])) FROM @DistinctGroupedColumnsResult FOR XML PATH('') ), 1, 1, '' );
DECLARE @GroupedColumnsNullReplaced VARCHAR(MAX);
IF(@DefaultNullValue IS NOT NULL)
SELECT @GroupedColumnsNullReplaced = STUFF ( ( SELECT DISTINCT CONCAT(', ISNULL(',QUOTENAME([row]),',',@DefaultNullValue,') AS ',QUOTENAME([row])) FROM @DistinctGroupedColumnsResult FOR XML PATH('') ), 1, 1, '' );
ELSE
SELECT @GroupedColumnsNullReplaced=@GroupedColumns;
DECLARE @ResultExpr VARCHAR(MAX) = CONCAT('
; WITH cte AS
(
SELECT ',@SelectExpression,', ',@GroupedColumns,'
FROM ',@TableName,'
PIVOT ( ',@AggregateExpression,' FOR ',@GroupingColumnName,' IN (',@GroupedColumns,') ) as p
)
, cte2 AS
(
SELECT ',@SelectExpression,', ',@GroupedColumnsNullReplaced,'
FROM cte
)
SELECT ',@SelectExpression,', ',REPLACE(@GroupedColumns,',','+'),' AS ',@TotalColumnName,', ',@GroupedColumns,'
FROM cte2;
');
IF(@IsExec = 1) EXEC(@ResultExpr);
ELSE SELECT @ResultExpr;
END;
使用示例:
select schema_id, type_desc, 1 as Item
into PivotTest
from sys.objects;
EXEC ExecutePivot 'PivotTest','type_desc','SUM(Item)','schema_id','[Total Items]','0',1;
【讨论】:
【参考方案3】:我们已经完成了很多与您的示例类似的工作。我们并不担心 SQL 注入,部分原因是我们可以完全控制被转换的数据——恶意代码不可能通过 ETL 进入我们的数据仓库。
一些想法和建议:
是否需要使用 nvarcahr(500) 列进行旋转?我们的是 varchar(25) 或数字,很难将破坏性代码从那里偷偷溜进去。 数据检查怎么样?似乎如果其中一个字符串包含“]”字符,那么它要么是黑客尝试,要么是数据无论如何都会炸毁你。 您的安全性有多强?系统是否已锁定,以至于 Malorey 无法将他的黑客行为偷偷潜入您的数据库(直接或通过您的应用程序)?哈。记住函数 QUOTENAME() 需要写所有这些。快速测试似乎表明像这样将其添加到您的代码中会起作用(您会收到错误,而不是删除的临时表):
SELECT
@columns =
STUFF
(
(
SELECT DISTINCT
', [' + quotename(ColumnB, ']') + ']'
FROM
#PivotTest
FOR XML PATH('')
), 1, 1, ''
)
这应该适用于枢轴(和非枢轴)情况,因为您几乎总是必须[括号]您的值。
【讨论】:
1) 我的测试样本是一个简单的样本。实际的列是 nvarchar(max)。我们目前没有这么大的数据,用于 PIVOT 的数据很少会达到 100,所以在这种情况下我可能会执行强制截断!好点子。 2)我在想'['和']'。我很想从数据中删除所有方括号,并将其作为此功能的限制。 3) 唯一可以添加这些数据的人是所谓的“超级用户”,但这还不足以让我安心。 引用名!我第一次看到它!完美的!这完全解决了问题。我正在手动添加引号。如果我删除它并使用 QUOTENAME 执行它,它将禁用该字段中的任何 SQL!谢谢!以上是关于SQL Server - 动态数据透视表 - SQL 注入的主要内容,如果未能解决你的问题,请参考以下文章