2010-03-11 56 views
4

我處於這樣一種情況:我得到一個以逗號分隔的VarChar作爲存儲過程的輸入。我想要做這樣的事情:你會如何努力使你的SQL查詢安全?

SELECT * FROM tblMyTable 
INNER JOIN /*Bunch of inner joins here*/ 
WHERE ItemID IN ($MyList); 

但是,你不能用一個varchar與IN聲明。有兩種方法來解決這個問題:

  1. (錯誤的方式)在字符串創建SQL查詢,像這樣:

    SET $SQL = ' SELECT * FROM tblMyTable INNER JOIN /*Bunch of inner joins here*/ WHERE ItemID IN (' + $MyList + ');

    EXEC($SQL);

  2. (該正確方法)創建一個臨時表,其中包含$MyList的值,然後在初始查詢中加入該表。

我的問題是:

選項2與創建臨時表,這是不理想的一個比較大的性能損失。

雖然選項1對SQL注入攻擊是開放的,但是由於我的SPROC是從已認證的源中調用的,因此真的是很重要嗎?只有可信賴的來源纔會執行此SPROC,因此如果他們選擇濫用數據庫,那就是他們的特權。

那麼,你會走多遠讓你的代碼安全?

+0

什麼是ItemID?如果它是數字,生成一個適當的字符串是微不足道的。如果是字符串,請準備一些愚蠢的字符串,如「no'id」(請注意') – TomTom 2010-03-11 13:23:54

+0

我將通過使用參數化查詢確保所有查詢都100%安全。 – rook 2010-03-11 16:20:44

回答

4

您正在使用什麼數據庫?在SQL Server中,您可以創建一個拆分函數,該拆分函數可以拆分一個長字符串並在秒以內返回一個表。您在查詢中使用表格函數調用,如同常規表格一樣(不需要臨時表格)

您需要創建一個拆分函數,或者如果您只有一個使用它。這是一個分裂的功能如何使用:

SELECT 
    * 
    FROM YourTable        y 
    INNER JOIN dbo.yourSplitFunction(@Parameter) s ON y.ID=s.Value 

I prefer the number table approach to split a string in TSQL但也有許多方法來拆分在SQL Server中的字符串,見前面的鏈接,這說明各的優點和缺點。

對於數字表的方法來工作,你需要做的這一次表的設置,這將創建一個包含從1到10000行的表Numbers

SELECT TOP 10000 IDENTITY(int,1,1) AS Number 
    INTO Numbers 
    FROM sys.objects s1 
    CROSS JOIN sys.objects s2 
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number) 

一旦Numbers表格設置,創建此分割功能:

CREATE FUNCTION [dbo].[FN_ListToTable] 
(
    @SplitOn char(1)  --REQUIRED, the character to split the @List string on 
    ,@List  varchar(8000)--REQUIRED, the list to split apart 
) 
RETURNS TABLE 
AS 
RETURN 
(

    ---------------- 
    --SINGLE QUERY-- --this will not return empty rows 
    ---------------- 
    SELECT 
     ListValue 
     FROM (SELECT 
        LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(@SplitOn, List2, number+1)-number - 1))) AS ListValue 
        FROM (
          SELECT @SplitOn + @List + @SplitOn AS List2 
         ) AS dt 
         INNER JOIN Numbers n ON n.Number < LEN(dt.List2) 
        WHERE SUBSTRING(List2, number, 1) = @SplitOn 
      ) dt2 
     WHERE ListValue IS NOT NULL AND ListValue!='' 

); 
GO 

您現在可以輕鬆地拆分CSV字符串轉換成表格,並加入就可以了:

select * from dbo.FN_ListToTable(',','1,2,3,,,4,5,6777,,,') 

OUTPUT:

ListValue 
----------------------- 
1 
2 
3 
4 
5 
6777 

(6 row(s) affected) 

您可以使用CSV字符串這樣,沒必要臨時表:

SELECT * FROM tblMyTable 
INNER JOIN /*Bunch of inner joins here*/ 
WHERE ItemID IN (select ListValue from dbo.FN_ListToTable(',',$MyList)); 
1

我個人更喜歡選項2,因爲來源是經過認證的,並不意味着你應該放鬆警惕。如果經過身份驗證的低級用戶仍然可以對尚未預期的數據庫執行命令,則可以開放潛在的權限升級。

您使用「可信來源」的短語 - 如果您承擔X檔案的處理並且不信任任何人,這可能會更好。

+1

「不信任任何人」 - 甚至不在StackOverflow上;) – APC 2010-03-11 13:41:46

0

選項3是確認列表中的每個項目在將字符串連接到SQL語句之前實際上是一個整數。

通過解析輸入字符串(例如,拆分爲數組),循環並將每個值轉換爲int來完成此操作,然後在連接回SQL語句之前自行重新創建列表。這將使您確信SQL注入不會發生。

連接已由應用程序創建的字符串比較安全,因爲您可以執行諸如檢查int之類的操作,但這也意味着您的代碼是以後續開發人員稍微修改的方式編寫的,因此會打開回來增加了SQL注入的風險,因爲他們沒有意識到這是你的代碼所抵禦的。如果你走這條路線,確保你評論你正在做什麼。

0

第三個選項:將值傳遞給數組中的存儲過程。然後,您可以在您的代碼中組裝逗號分隔的字符串並使用動態SQL選項,或者(如果您的RDBMS風格允許它)直接在SELECT語句中使用該數組。

1

如果有人竊聽數據庫,您可能仍然在接聽電話。

類似於選項二的一個很好的選擇是使用函數在CSV列表中創建一個內存表。它速度相當快,並提供了選項二的保護。然後,該表可以連接到Inner Join,例如

CREATE FUNCTION [dbo].[simple_strlist_to_tbl] (@list nvarchar(MAX)) 
    RETURNS @tbl TABLE (str varchar(4000) NOT NULL) AS 
BEGIN 
    DECLARE @pos  int, 
      @nextpos int, 
      @valuelen int 

    SELECT @pos = 0, @nextpos = 1 

    WHILE @nextpos > 0 
    BEGIN 
     SELECT @nextpos = charindex(',', @list, @pos + 1) 
     SELECT @valuelen = CASE WHEN @nextpos > 0 
           THEN @nextpos 
           ELSE len(@list) + 1 
         END - @pos - 1 
     INSERT @tbl (str) 
     VALUES (substring(@list, @pos + 1, @valuelen)) 
     SELECT @pos = @nextpos 
    END 
    RETURN 
END 

然後在加入:

tblMyTable INNER JOIN 
simple_strlist_to_tbl(@MyList) list ON tblMyTable.itemId = list.str 
0

你爲什麼不寫一個CLR分割功能,這將做所有的工作好,易於?您可以編寫用戶定義的表函數,這些函數將返回一個使用.net infructure進行字符串拆分的表。地獄在SQL 2008中,你甚至可以給他們提示,如果他們返回以任何方式排序的字符串......就像升序或可以幫助優化器的東西? 或者也許你不能做CLR集成那麼你必須堅持tsql,但我個人會去CLR解決方案