2011-12-19 57 views
5

這可以很容易地注入到這裏,因爲@ID參數通過輸入它可以實際上是此SQL語句中的任何內容,但是,如何防止此漏洞?從SQL注入中製作SQL Server存儲過程安全

我更喜歡在此級別而不是應用程序級別專門防止此漏洞,有什麼建議嗎?

CREATE PROCEDURE [dbo].[GetDataByID] 
@ID bigint, 
@Table varchar(150) 
AS 
BEGIN 

Declare @SQL Varchar(1000) 

SELECT @SQL = 'SELECT * FROM ' + @Table + ' WHERE ID = ' + CONVERT(varchar,@ID) 

SET NOCOUNT ON; 

EXEC(@sql) 
END 
+1

只是一個不會影響很多東西的小東西,但是不是'@ Table'參數是這裏的問題嗎? '@ ID'是一個'bigint',所以當你到達構建動態SQL語句的位置時它只能是一個數字,對吧? – 2011-12-19 19:52:48

+0

我想都是.. – 2011-12-19 19:54:24

回答

9

檢查this page,它有一個美好的指南動態SQL和選項來安全地執行他們

在你的情況應該是這樣的:

SELECT @SQL = N'SELECT * FROM ' + quotename(@Table) + N' WHERE ID = @xid' 
EXEC sp_executesql @SQL, N'@xid bigint', @ID 
+0

+1鏈接到Sommarskog。 – Oded 2011-12-19 19:46:09

+1

+1是第一次真正瞭解我在StackOverflow上遇到的人的活動;) – Tao 2011-12-19 19:48:09

+0

lol!你好! :) – 2011-12-19 19:50:03

1

1)創建新表將具有身份PK幷包含字符串表名
2)在您的過程中插入所有/只有有效表,您將允許
3)使用此int身份PK作爲存儲過程的輸入參數值(TableID)
4)在過程中,只需從給定身份PK查找字符串值(表名),並且可以安全地在查詢中連接查找的字符串。 5)你的WHERE子句是罰款,因爲你通過一個int

0

我會建議完全避免動態sql。是的問題如下:

  • 明顯的注入附加場景
  • 二進制注入攻擊是更聰明,可以繞過傳統的串逃逸
  • 表現是大單 - SQL服務器被設計爲在管理執行計劃存儲過程,它們將比動態構建的查詢運行得更快。如果您使用的是動態sql,根本沒有使用存儲過程的真正好處。如果你想要從多個表中選擇代碼的靈活性,你應該考慮一個ORM或其他東西來使你的代碼更容易。考慮到你必須動態地傳遞表格,那麼我甚至會說沒有像上面這樣的過程,而不同的解決方案是最好的選擇。如果您只是針對SQL(即沒有ORM)編寫代碼,那麼生成單獨特效代碼甚至會是更好的選擇。

備註 QUOTENAME不保證您注射安全。截斷注射仍然是可能的。使用前請閱讀http://msdn.microsoft.com/en-us/library/ms161953.aspx

+0

感謝您的回覆,但您能否發佈有關QUOTENAME的工作鏈接? – 2011-12-19 19:59:57

+0

不知道發生了什麼:)現在應該工作。 – Gats 2011-12-19 20:03:12

0

儘管我一般建議不要使用動態sql,在這種情況下,我認爲你可以通過檢查@Table變量是否包含有效的表名來避開它。

  • 的問題是,如果你打算允許模式名稱和/或跨數​​據庫查詢,我假設你不想出去的DB(或服務器)的在這裏,但也允許不同模式的(AdventureWorks顯示如何使用它們)
  • 您可能還想包含@Table的視圖。
  • 如果您還檢查了發現的對象是否實際上有ID列,並且如果不是,則拋出'userfriendly'錯誤,這可能是'很好'。儘管如此。

只要將QuoteName()放在@table周圍就不會保護你免受一切侵害。雖然功能很棒,但它還遠沒有完美。恕我直言,你最好的選擇是解析@Table變量,檢查它的內容是否有效,然後根據獲得的部分創建動態sql。 我開始做的最上方和令人驚訝的還有它需要檢查的東西看起來如此簡單的手=)

CREATE PROCEDURE [dbo].[GetDataByID] ( 
             @ID bigint, 
             @Table nvarchar(300) 
            ) 
AS 

DECLARE @sql nvarchar(max) 

DECLARE @server_name sysname, 
     @db_name  sysname, 
     @schema_name sysname, 
     @object_name sysname, 
     @schema_id int   

SELECT @server_name = ParseName(@Table, 4), 
     @db_name  = ParseName(@Table, 3), 
     @schema_name = ParseName(@Table, 2), 
     @object_name = ParseName(@Table, 1) 

IF ISNULL(@server_name, @@SERVERNAME) <> @@SERVERNAME 
    BEGIN 
     RaisError('Queries are restricted to this server only.', 16, 1) 
     Return(-1) 
    END 

IF ISNULL(@db_name, DB_Name()) <> DB_Name() 
    BEGIN 
     RaisError('Queries are restricted to this database only.', 16, 1) 
     Return(-1) 
    END 


IF @schema_name IS NULL 
    BEGIN 
     IF NOT EXISTS (SELECT * 
          FROM sys.objects 
         WHERE name = @object_name 
          AND type IN ('U', 'V')) 
      BEGIN 
       RaisError('Requested @Table not found. [%s]', 16, 1, @object_name) 
       Return(-1) 
      END 

     SELECT @sql = 'SELECT * FROM ' + QuoteName(@object_name) + ' WHERE ID = @ID' 
    END 
ELSE 
    BEGIN 

     SELECT @schema_id = Schema_id(@schema_name) 

     IF @schema_id IS NULL 
      BEGIN 
       RaisError('Unrecognized schema requested [%s].', 16, 1, @schema_name) 
       Return(-1) 
      END 

     IF NOT EXISTS (SELECT * 
          FROM sys.objects 
         WHERE name = @object_name 
          AND schema_id = @schema_id 
          AND type IN ('U', 'V')) 
      BEGIN 
       RaisError('Requested @Table not found. [%s].[%s]', 16, 1, @schema_name, @object_name) 
       Return(-1) 
      END 

     SELECT @sql = 'SELECT * FROM ' + QuoteName(@schema_name) + '.' + QuoteName(@object_name) + ' WHERE ID = @ID' 
    END 

EXEC sp_executesql @stmt = @sql, 
        @params = N'@ID bigint', 
        @ID  = @ID 

Return(0)  

同前編譯,但您可能需要以消除一些錯誤,因爲我沒」檢查所有的代碼路徑就行了。