2009-09-17 126 views
13

對不起,但這包含了我用來測試場景的所有SQL,希望能夠讓我清楚自己在做什麼。SQL Server - 動態PIVOT表 - SQL注入

我建立一些動態SQL產生透視表在SQL Server 2005

下面的代碼來做到這一點。用各種選擇顯示原始數據使用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失敗。

所以我的問題是,有誰知道一種方法來執行動態PIVOT而不冒着SQL注入攻擊風險?

回答

15

我們已經完成了很多類似於您的示例的工作。我們並不擔心SQL禁令,部分原因是我們完全控制了數據的完整控制 - 惡意代碼無法通過ETL進入我們的數據倉庫。

的幾點思考和建議:

  • 你需要用nvarcahr(500)列轉動?我們是varchar(25)或數字,並且通過那裏潛行破壞代碼是相當困難的。
  • 數據檢查如何?看起來像是其中一個字符串包含一個「]」字符,它無論是黑客攻擊還是數據都會炸燬你。
  • 您的安全性有多強大?系統是否被鎖定,以至於Malorey無法將他的黑客入侵數據庫(直接或通過您的應用程序)?

哈。花了所有的時間來記住函數QUOTENAME()。快速測試似乎表明它添加到您的代碼,像這樣將工作(你會得到一個錯誤,而不是下降臨時表):

SELECT 
     @columns = 
     STUFF 
     (
       (
         SELECT DISTINCT 
           ', [' + quotename(ColumnB, ']') + ']' 
         FROM 
           #PivotTest 
         FOR XML PATH('') 
       ), 1, 1, '' 
     ) 

這應該樞軸(和逆透視)情況下工作,因爲你幾乎總是需要[括號]你的值。

+0

1)我的測試樣品是一個簡單的。實際的列是nvarchar(max)。目前我們沒有數據大小的數據,而用於PIVOT的數據很少會達到100個,所以我可以在這種情況下執行強制截斷!好想法。 2)我正在考慮'['和']'。我很想從數據中去掉所有的方括號,並將其作爲此功能的限制。 3)唯一能夠添加這些數據的人稱爲「超級用戶」,但這不足以讓我安心。 – 2009-09-17 16:37:05

+1

QUOTENAME!我第一次看到它!完善!這完全解決了這個問題。我手動添加QUOTES。如果我刪除它並使用QUOTENAME執行它,它將禁用該字段中的任何SQL!謝謝! – 2009-09-17 16:38:22

0

重構的比特...

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 @[email protected]; 

    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;