13

Entschuldigung für die lange Frage, aber das enthält alle SQL, die ich verwendet habe, um das Szenario zu testen, um hoffentlich klar zu machen, was ich mache.SQL Server - Dynamische PIVOT Tabelle - SQL Injection

Ich bin aufbauen einige dynamischen SQL eine Pivot-Tabelle in SQL Server 2005

Im Folgenden Code, dies zu tun, ist zu produzieren. Mit verschiedenen Auswahlmöglichkeiten zeigen die Rohdaten die Werte mit GROUP BY und die Werte in einem PIVOT wie ich sie haben möchte.

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 

Immer wenn ich irgendeine dynamische SQL erzeuge, bin ich immer auf SQL Injection Attacken aufmerksam. Daher habe ich die folgende Zeile mit den anderen INSERT-Anweisungen hinzugefügt.

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

Wenn ich jetzt die SQL laufen, und siehe da, fällt der EXEC Teil damit die #PivotTest Tabelle der letzte SELECT nicht machen.

Meine Frage ist also, weiß jemand eine Möglichkeit, eine dynamische PIVOT durchzuführen, ohne SQL Injection-Angriffe zu riskieren?

Antwort

15

Wir haben eine Menge Arbeit wie in Ihrem Beispiel gemacht. Wir haben uns keine Sorgen um SQL-Injection gemacht, teilweise weil wir die vollständige und vollständige Kontrolle über die Daten haben, die geschwenkt werden - es gibt keinen Weg, wie schädlicher Code durch ETL in unser Data Warehouse gelangen könnte.

Einige Gedanken und Ratschläge:

  • Stehen Sie mit nvarcahr (500) Spalten schwenken erforderlich? Unsere sind varchar (25) oder Numerik, und es wäre ziemlich schwer, dort schädlichen Code einzuschleusen.
  • Wie wäre es mit der Datenprüfung? Scheint es so, als ob eine dieser Zeichenfolgen ein "]" - Zeichen enthält, ist es entweder ein Hack-Versuch oder Daten, die dich sowieso in die Luft jagen.
  • Wie robust ist Ihre Sicherheit? Ist das System so gesperrt, dass Malorey seine Hacks nicht in Ihre Datenbank einschleusen kann (entweder direkt oder über Ihre Anwendung)?

Hah. Es brauchte alles zu schreiben, um sich an die Funktion QUOTENAME() zu erinnern. Ein schneller Test würde darauf hindeuten, dass es zu Ihrem Code hinzufügen, wie so funktionieren würde (Sie erhalten eine Fehlermeldung erhalten, nicht fallen gelassen temporäre Tabelle):

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

Dies sollte für Dreh arbeiten (und Entpivotisierung) Situationen, da du deine Werte fast immer [bracke].

+0

1) ist My Testprobe einfach. Die tatsächlichen Spalten sind nvarchar (max). Wir haben derzeit keine Daten dieser Größe, und die Daten, die für den PIVOT verwendet werden würden, wären selten so groß wie 100, daher kann ich in diesem Fall eine erzwungene Kürzung durchführen! Großartige Idee. 2) Ich dachte an 'und'. Ich bin versucht, alle eckigen Klammern aus den Daten zu streichen und nur als Einschränkung dieser Funktionalität. 3) Die einzigen Leute, die diese Daten hinzufügen können, sind sogenannte "Super User", dies ist jedoch nicht genug, um mir ein Gefühl der Sicherheit zu geben. –

+1

QUOTENAME! Das erste Mal habe ich es gesehen! Perfekt! Dies löst das Problem vollständig. Ich habe die QUOTES manuell hinzugefügt.Wenn ich dies entferne und es mit QUOTENAME mache, wird jedes SQL in diesem Feld deaktiviert! DANKE! –

0

Ein bisschen 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; 

Anwendungsbeispiel:

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;