2009-09-03 94 views
3

這個存儲過程使用sp_executesql執行帶有參數的sql。
從sql注入安全嗎?該存儲過程是否安全的從sql注入?

create procedure ExecutePeopleFilter 
    (@lastNameFilter varchar(20), 
    @companyNameFilter varchar(20), 
    @ageFilter int, 
    @dateFilter datetime) 
as 
begin 
    declare @sql varchar(4000) 
    declare @params varchar(1000) 
    declare @whereClause varchar(1000) 

    set @whereClause = '' 

    if ISNULL(@lastNameFilter,'') <> '' 
    begin 
     if (LEN(@whereClause) <> 0) set @whereClause += ' and ' 
     if (LEN(@lastNameFilter) < 20) set @lastNameFilter += '%' 
     set @whereClause += 'LastName like @lastName ' 
    end 

    if ISNULL(@companyNameFilter,'') <> '' 
    begin 
     if (LEN(@whereClause) <> 0) set @whereClause += ' and ' 
     if (LEN(@companyNameFilter) < 20) set @companyNameFilter += '%' 
     set @whereClause += 'CompanyName like @companyName ' 
    end 

    if @ageFilter is not null 
    begin 
     if (LEN(@whereClause) <> 0) set @whereClause += ' and '  
     set @whereClause += 'Age = @age ' 
    end 

    if @dateFilter is not null 
    begin 
     if (LEN(@whereClause) <> 0) set @whereClause += ' and ' 
     set @whereClause += 'StartDate = @startDate ' 
    end 


    set @sql = 'select FirstName, LastName, CompanyName, Age, StartDate 
     from People' 
    if (LEN(@whereClause) <> 0) set @sql += ' where ' + @whereClause 

    set @params = '@lastName varchar(20), 
     @companyName varchar(20), 
     @age int, 
     @startDate datetime' 

    execute sp_executesql @sql, @params, 
     @lastName = @lastNameFilter, 
     @companyName = @companyNameFilter, 
     @age = @ageFilter, 
     @startDate = @dateFilter 
end 

回答

2

漂亮多了。

防止SQL注入的關鍵是通過「已批准」機制正確處理參數並避免字符串連接。

您的代碼不會使用參數生成一個字符串:它們通過sp_executesql進行分隔和清理。

無論你做這種方式是一個不同的問題...其他的答案顯示

+0

標記爲答案,因爲它是第一個直接回答問題的人。儘管如此,別人的回答也提供了寶貴的意見。 – DyingCactus 2009-09-04 01:27:24

3

爲什麼在存儲過程中這樣做?一個更好的解決方案可能是在調用存儲過程之前在客戶端攻擊它,轉義字符串並檢查長度。像MS Enterprise DAAB(​​.NET)這樣的庫提供了方便的方法來實現這一點,方法是在將參數添加到命令對象時指定數據類型和參數長度。

+1

...或者乾脆使用LinqToSQL。 – 2009-09-03 15:55:21

+0

關於在存儲過程之外執行此操作的好處。在.NET 3.5中,我通過使用LINQ構建查詢來做到這一點 - 非常容易。 – RichardOD 2009-09-03 15:57:09

+0

@ DyingCactus-看看這個:http://www.albahari.com/nutshell/predicatebuilder.aspx – RichardOD 2009-09-03 15:59:31

1

你不需要動態SQL使可選的WHERE子句...只需使用:

WHERE ((@x IS NULL) OR (@x = ...)) AND ... 

應該更快也和串溢出,注射,或任何沒有風險。

像這樣:

CREATE PROCEDURE ExecutePeopleFilter 
    (
    @lastNameFilter varchar(20), 
    @companyNameFilter varchar(20), 
    @ageFilter int, 
    @dateFilter datetime 
) 
AS 
    BEGIN 
    SELECT FirstName, LastName, CompanyName, Age, StartDate 
     FROM People 
     WHERE (
      (ISNULL(@lastNameFilter, '') = '') 
      OR (LastName LIKE @lastNameFilter+'%') 
      ) 
     AND (
      (ISNULL(@companyNameFilter, '') = '') 
      OR (LastName LIKE @companyNameFilter+'%') 
      ) 
     AND (
      (@ageFilter IS NULL) 
      OR (Age = @ageFilter) 
      ) 
     AND (
      (@dateFilter IS NULL) 
      OR (StartDate = @dateFilter) 
      ) ; 
    END 
+1

更快? WHERE子句中的「OR」非常慢。 – 2009-09-03 16:08:19

+0

如果你看執行計劃,你會發現這個處理非常好。更快,因爲沒有任何構建,解析和編譯每個調用所涉及的查詢。 – Lucero 2009-09-03 16:13:05

+0

好吧,所以我在我的工作站上的本地SQL Server實例上創建了一個基準測試。用發生器插入10'000'000行(1000萬)隨機數據;在查詢分析器中使用任何混合的已使用參數和NULL參數在SP中查詢返回00:00:00。所以我想這種方法不是「很慢」,是嗎? – Lucero 2009-09-03 17:03:52

1

你從不將任何東西,除了衆所周知的,硬編碼值轉換爲SQL語句發給數據庫引擎,所以目前已知的所有SQL注入方法都是安全的(並且對未來的攻擊應該是健壯的)。但是,它確實存在其他問題(如未聲明@startDate)。

+0

感謝但不是@startDate在「set @params」語句中聲明? – DyingCactus 2009-09-03 16:55:38

1

是的,你使用字符串concat和sp_executesql是正確的,SQL注入不會是一個問題。

不,因爲LINQ是新的熱點並不意味着它是正確的解決方案。

這就是說,你的varchar(20)參數可以很容易地溢出,你可能想要將它們提高一點,至少達到實際字段的大小。

+0

謝謝。我同意你的LINQ評論。無論如何,即使有更好的方法來獲得這個例子的結果,我也想知道在其他方法不實際的地方使用該技術是否安全。 – DyingCactus 2009-09-03 17:27:53

0

認爲所以。至少,它看起來好吧。但這可能不是我會怎麼做的。

安全的關鍵租戶之一是永遠不會永遠不會寫你自己的安全代碼。您總是想要儘可能地依靠您的平臺提供的內置機制。你通過使用sp_executesql來做到這一點,但我希望你能做得更多。在WHERE子句中

create procedure ExecutePeopleFilter 
    (@lastNameFilter varchar(20) = NULL, 
    @companyNameFilter varchar(20) = NULL, 
    @ageFilter int = NULL, 
    @dateFilter datetime = NULL) 
as 
begin 

    if (LEN(@lastNameFilter) < 20) set @lastNameFilter += '%' 
    if (LEN(@companyNameFilter) < 20) set @companyNameFilter += '%' 

    SELECT FirstName, LastName, CompanyName, Age, StartDate 
    FROM People 
    WHERE LastName LIKE COALESCE(@lastNameFilter, LastName) 
     AND CompanyName LIKE COALESCE(@companyNameFilter, CompanyName) 
     AND Age   = COALESCE(@ageFilter, Age) 
     AND StartDate = COALESCE(@dateFilter, StartDate) 

end 

無動態SQL需要,並沒有 「或」 S:


根據要求,我可能會做更多這樣的。

+0

謝謝。你能描述一下你如何去做嗎? – DyingCactus 2009-09-03 17:22:13

+0

coalesce版本看起來不錯,但如果有任何過濾列的值爲空值的行將不起作用。例如:如果@companyNameFilter =「X」和@dateFilter爲null,並且有一排那家公司,但它的起始日期爲null,該行將不會被包含在結果中。但是,如果你所有的過濾列都是NOT-NULL,那麼這是動態sql版本的替代,而不是OR方法。 – DyingCactus 2009-09-03 18:36:45

+0

你也可以將COALESCE()也放在左邊,但是你的表現就像「OR」一樣。所以這可以取決於你的數據是什麼樣子。 – 2009-09-03 19:17:25

0

即使是......「啊」。

爲什麼不改爲使用臨時表,以滿足您的非可選參數的元素結果的ID來填充它,然後消除基於已指定的參數從臨時表中的其他人呢?一旦你完成了,只需加入你正在尋找的結果集。

CREATE TABLE #People 
    (personid int) 
INSERT INTO #People SELECT personid FROM people 
IF NOT @lastNameParam IS NULL 
    DELETE FROM #People WHERE personid NOT IN (SELECT personid FROM people WHERE lastname LIKE @lastNameParam + '%') 
-- And so on... 
+0

你能解釋一下「呃」是什麼意思嗎?代碼技術是錯誤的,風格錯誤,效率差,還是隻看起來醜陋?我不確定明確的臨時表會執行多少,特別是對於大表和/或比給出的簡單示例更復雜的邏輯。除非絕對必要,否則我寧願讓sql在低級別中決定如何獲取數據。 – DyingCactus 2009-09-03 17:36:16