代码之家  ›  专栏  ›  技术社区  ›  Robin Day

SQL Server-动态透视表-SQL注入

  •  15
  • Robin Day  · 技术社区  · 15 年前

    很抱歉问了这么长一个问题,但这包含了我用来测试场景的所有SQL,希望能弄清楚我在做什么。

    我构建了一些动态SQL来在SQL Server2005中生成透视表。

    下面是这样做的代码。通过各种选择,显示原始数据、使用group by的值以及我想要的数据透视中的值。

    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表,从而使最后一次选择失败。

    3 回复  |  直到 6 年前
        1
  •  15
  •   Philip Kelley    15 年前

    我们做了很多类似于您的例子的工作。我们不担心SQL的影响,部分原因是我们对数据透视具有完全和完全的控制——恶意代码无法通过ETL进入我们的数据仓库。

    一些想法和建议:

    • 是否需要以nvarcahr(500)列为轴?我们的代码是varchar(25)或numerics,很难从中偷袭破坏性的代码。
    • 数据检查怎么样?似乎如果这些字符串中有一个包含“]”字符,那么它要么是黑客攻击,要么是数据,无论如何都会对您造成破坏。
    • 您的安全性如何?系统是否被锁定,这样马洛里就不能(直接或通过你的应用程序)将他的黑客潜入你的数据库?

    哈哈。要记住函数QuoteName(),需要编写所有这些内容。快速测试似乎表明,像这样将其添加到代码中是可行的(您将得到一个错误,而不是一个删除的临时表):

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

    这应该适用于透视(和非透视)情况,因为您几乎总是需要[bracket]您的值。

        2
  •  0
  •   AAATechGuy    9 年前

    有点重构…

    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
  •  -1
  •   pushkin Void Star    6 年前
    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)