13

Xin lỗi vì câu hỏi dài nhưng điều này chứa tất cả các câu lệnh SQL mà tôi đã sử dụng để kiểm tra kịch bản để hy vọng nó rõ ràng như những gì tôi đang làm.Máy chủ SQL - Bảng PIVOT động - SQL Injection

Tôi đang xây dựng một số SQL động để tạo bảng PIVOT trong SQL Server 2005.

Dưới đây là mã để thực hiện việc này. Với các lựa chọn khác nhau cho thấy dữ liệu thô các giá trị sử dụng GROUP BY và các giá trị trong PIVOT như tôi muốn chúng.

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 

Bất cứ khi nào tôi tạo ra bất kỳ SQL động nào, tôi luôn nhận thức được các cuộc tấn công SQL Injection. Do đó tôi đã thêm dòng sau đây với các câu lệnh INSERT khác.

INSERT INTO #PivotTest (ColumnA, ColumnB, ColumnC) VALUES('A', 'FOO])) PVT; DROP TABLE #PivotTest;SELECT ((GETDATE()--', 1) 

Khi tôi chạy SQL, thấp và nhìn, phần EXEC thả bảng #PivotTest do đó làm cho SELECT cuối cùng thất bại.

Vì vậy, câu hỏi của tôi là, có ai biết cách để thực hiện một PIVOT động mà không mạo hiểm các cuộc tấn công SQL Injection không?

Trả lời

15

Chúng tôi đã thực hiện rất nhiều công việc tương tự như ví dụ của bạn. Chúng tôi đã không lo lắng về việc bắt buộc SQL, một phần vì chúng tôi có toàn quyền và toàn quyền kiểm soát dữ liệu được xoay vòng - không có cách nào mã độc hại có thể nhận được thông qua ETL vào kho dữ liệu của chúng tôi.

Một số suy nghĩ và lời khuyên:

  • Bạn đang cần thiết để trục với nvarcahr (500) cột? Chúng ta là varchar (25) hoặc số, và nó sẽ là khá khó khăn để sneak mã gây hại trong thông qua đó.
  • Kiểm tra dữ liệu như thế nào? Có vẻ như nếu một trong những chuỗi đó chứa ký tự "]", thì đó là một nỗ lực hack hoặc dữ liệu sẽ làm bạn thất vọng.
  • Bảo mật của bạn mạnh đến mức nào? Hệ thống có bị khóa không mà Malorey không thể lẻn vào trong cơ sở dữ liệu của bạn (trực tiếp hoặc thông qua ứng dụng của bạn)?

Hah. Nó đã viết tất cả những gì để nhớ hàm QUOTENAME(). Một test nhanh sẽ dường như chỉ ra rằng thêm nó vào mã của bạn như vậy sẽ làm việc (Bạn sẽ nhận được một lỗi, không phải là một bảng temp giảm):

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

này nên làm việc cho trục (và UNPIVOT) tình huống, vì bạn hầu như luôn phải [gắn khung] các giá trị của mình.

+0

1) mẫu thử nghiệm của tôi là một trong những đơn giản. Các cột thực tế là nvarchar (max). Hiện tại chúng tôi không có dữ liệu về kích thước và dữ liệu sẽ được sử dụng cho PIVOT hiếm khi có thể lên đến 100 vì vậy tôi có thể thực hiện một lệnh cắt bắt buộc trong trường hợp này! Ý tưởng tuyệt vời. 2) Tôi đã suy nghĩ về '[' và ']'. Tôi bị cám dỗ để loại bỏ tất cả các dấu ngoặc vuông khỏi dữ liệu và chỉ có điều đó như là một hạn chế của chức năng này. 3) Những người duy nhất có thể thêm dữ liệu này được gọi là "Người dùng siêu", tuy nhiên, điều này là không đủ để tôi yên tâm. –

+1

QUOTENAME! Lần đầu tiên tôi nhìn thấy nó! Hoàn hảo! Điều này hoàn toàn giải quyết được vấn đề. Tôi đã thêm QUOTES theo cách thủ công.Nếu tôi loại bỏ điều này và làm điều đó bằng cách sử dụng QUOTENAME nó sẽ vô hiệu hóa bất kỳ SQL trong lĩnh vực đó! CẢM ƠN BẠN! –

0

Một chút refactoring ...

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; 

Cách sử dụng Ví dụ:

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;