[轉載] 編寫安全的 Transact-SQL

簡介

關於如何以安全的方式部署 SQL Server,存在大量很好的信息源。但是,這些資源的目標用戶通常都是那些對已經開發好的應用程序執行保護任務的數據庫管理員。另外,還有很多內容討論瞭如何編寫安全的 .NET 和 ASP.NET 代碼,其中包括訪問 SQL Server 的 .NET 代碼。然而,很多這樣的資源關注的是在應用服務器上運行的數據訪問代碼,而不是在 SQL Server 中執行的 Transact-SQL (T-SQL) 代碼。本專欄則將注意力投射到如何開發在 SQL Server 上安全運行的 T-SQL 代碼。

保護開發 SQL Server 的安全

開發安全 T-SQL 的第一步是保護開發 SQL Server 的安全。爲什麼要想方設法地鎖定一個不保存真實數據的 SQL Server 實例,而從不將它展示給最終用戶呢?這是因爲,這樣會強制您編寫更安全的 T-SQL,並且當將您的應用程序部署到生產中時,也會更加容易地保護該應用程序。下面是幾個具體的步驟,採用這些步驟您就可以快速保護開發服務器:

在開發或測試 SQL Server 中,至少應該有一個正在運行的、最新的 Service Pack 和 SQL 安全修補程序,這樣才能確保您的客戶能夠在此 SQL Server 版本上成功運行您的應用程序。

默認情況下,SQL Server 2000 Service Pack 3a 會禁用稱爲“交叉數據庫所有權鏈接”的不安全功能。在開發服務器上安裝 SP3 時,如果讓該 Service Pack 禁用“交叉數據庫所有權鏈接”,則有助於驗證您正在基於安全的服務器配置編寫 T-SQL 代碼。

找出開發 SQL Server 上常見的安全配置問題的一種簡便方法是針對該服務器運行 Microsoft Baseline Security Analyzer。除了這種方法之外,還可以利用本專欄“參考資料”部分列出的資源;這些資源提供了一些附加步驟,可幫助您保護開發 SQL Server 的安全。

通常情況下,保護開發服務器安全的最佳方法,就好似它正在生產環境中運行那樣對它進行保護。您離這個目標越接近,那麼就可以越自信於您開發的代碼可以在一個安全的生產環境中正常運行。

以最低權限帳戶身份進行開發

在開發過程中,大家都着迷於使用具有 sysadmin 或 dbo SQL Server 權限的帳戶,直到部署之前才轉換爲一個權限更低的帳戶。使用這種方法存在着一個問題:將設計人員的權限集還原爲最低的所需權限集與在開發應用程序過程中編寫這些權限集相比,前者要困難得多。

鑑於部署應用程序之前您要決定可以取消哪些權限,所以請不要使用 SQL sysadmin 帳戶開發 T-SQL 代碼。如果使用 SQL sysadmin 帳戶,可能會造成這樣的結果,即應用程序會以比所需權限更多的特權帳戶運行。因此,開發時請改爲使用具有最低權限的帳戶。

使用這樣的帳戶進行開發時,您會逐漸地升高授予的特定權限,以 EXEC(執行)一些必需的存儲過程、從某些表進行 SELECT(選擇)等。請編寫這些 GRANT 語句,以便可以將同樣的最低權限輕鬆部署到生產環境中,而不會出現任何基於猜測的操作。

這種理念同樣適用於測試。執行臨時測試以及結構更加複雜的測試時,所使用帳戶擁有的權限集和用戶權限應該與在生產環境中所使用帳戶擁有的權限集和用戶權限完全相同。

在開發過程中使用最低權限帳戶的另一個優點在於,您可以避免不小心編寫出需要危險權限或過高權限的代碼。例如,假設您需要在 T-SQL 中與第三方 COM 組件進行交互。爲此,一種方法是發送一個 SQL 批處理命令,它直接調用 sp_OACreatesp_OAMethod 來操縱該 COM 對象。在應用程序使用 sysadmin 帳戶連接 SQL Server 的開發環境中,上述方法效果很好。但是,當您嘗試將已經開發完成的應用程序準備用於生產部署時,您就會發現如果使用權限較低的帳戶,那麼該方法不會奏效。爲了讓該應用程序能夠使用非 sysadmin 帳戶在生產環境中正常運行,您必須針對 sp_OACreate 顯式授予 EXECUTE 權限。請考慮一下,如果某個用戶最終找到了一個方法,可以使用該應用程序登錄執行任意代碼,並利用此權限針對 SQL Server 實例化一個類似 Scripting.FileSystemObject 的 COM 對象,將會產生怎樣的安全隱患?

遵照保護 T-SQL 的最佳方法

防禦一系列稱爲“SQL 注入式”的安全漏洞是至關重要的。通常情況下,您會使用多層防護來抵禦 SQL 注入式攻擊:

執行用戶提供輸入的驗證(例如,強制數據類型和最大字符串長度)。

轉義對數據庫引擎可能具有特殊意義的字符序列。在 T-SQL 中,注入式攻擊中最常用的兩個字符串爲單引號字符 (') 和註釋字符序列 (--)。

在 T-SQL 語句中,請不要將用戶提供的值進行內聯。請改爲使用預處理語句和參數化。

SQL 注入式攻擊在其他一些地方有詳細的說明,所以在此我就不花大量時間來討論這個問題的細節了。但是要強調一點,SQL 注入式問題並不只限於在應用層構建的 T-SQL 查詢。只要執行一個部分由用戶提供值構建的 T-SQL 查詢,就可能會發生 SQL 注入式問題。這就是說,一個在內部構建查詢字符串、並通過 EXEC() 命令或 sp_executesql 存儲過程執行該查詢的存儲過程也可能會受到攻擊。請參閱“參考資料”部分獲得一些資源鏈接,這些資源提供了各種 SQL 注入式攻擊類型的示例,還提供了一些保護代碼免受這些攻擊的技巧。

另一個最佳方法是避免針對基表授予權限。對於您希望用戶能夠執行的查詢,您應該將其打包在存儲過程中,並只對這些存儲過程授予 EXECUTE 權限。如果您按照本指南進行操作,即使用戶設法跳過了您的應用程序,直接登錄到數據庫,他們也無法迴避您已經在存儲過程中構建的任何數據驗證、審覈、業務規則或者行級安全限制。

瞭解具有獨特安全考慮事項的 T-SQL 命令

有一些 T-SQL 命令和擴展,它們具有自己獨特的安全考慮事項。其中一個是 sp_OACreate 及其相關的系統過程系列(例如 sp_OAMethod、sp_OAProperty 等)。以前,我們曾經研究過一個潛在的安全問題,通過授予應用程序登錄直接訪問這些過程的權限,會帶來該安全問題。爲了避免此問題的發生,請絕對不要編寫直接調用 sp_OA 過程的應用程序代碼,而要將對這些過程的所有引用都打包在您自己的 T-SQL 存儲過程中,並只授予訪問這些包裝存儲過程的權限。另外,請不要允許應用程序代碼將 COM 對象或方法的名稱作爲可由包裝過程無條件調用的字符串進行傳遞。

另一個具有獨特安全風險集的內置 SQL Server 擴展爲 xp_cmdshell。這個系統存儲過程可以運行任何可執行文件或系統命令。由於一些很顯然的原因,xp_cmdshell 上的 EXEC 權限默認情況下僅爲 sysadmin 用戶,必須顯示地爲其他用戶授予該權限。如果您需要應用程序在 SQL Server 上運行某個特定的命令或實用程序,則請注意,不要在應用程序中構建一個 xp_cmdshell 直接訪問的相關內容。這樣的風險與直接訪問 sp_OACreate 的風險相似。一旦爲某個帳戶授予了 xp_cmdshell 的 EXEC 權限,該帳戶不但能夠執行您希望其訪問的特定命令,而且能夠執行成百上千個操作系統命令和其他可執行文件。與 sp_OACreate 相似,始終將 xp_cmdshell 調用打包在另一個存儲過程中,避免直接在 xp_cmdshell 上授予 EXECUTE 權限。

您還應該避免將任何用戶提供的字符串參數或者應用程序提供的字符串參數與將要通過 xp_cmdshell 執行的命令進行串聯。如果無法達到上述要求,則必須瞭解,有一個專門針對 xp_cmdshell 的潛在的代碼注入式攻擊(至少在 SQL Server 中)。以下面的存儲過程爲例:

CREATE PROCEDURE usp_DoFileCopy @filename varchar(255) AS
DECLARE @cmd varchar (8000)
SET @cmd = 'copy //src/share/' + @filename + ' //dest/share/'
EXEC master.dbo.xp_cmdshell @cmd
GO
GRANT EXEC ON usp_DoFileCopy TO myapplogin

通過將 xp_cmdshell 調用打包在您自己的存儲過程中並只針對該 usp_DoFileCopy 存儲過程授予 EXEC 權限,您已經阻止了用戶直接調用 xp_cmdshell 以執行任意命令。然而,以下面的 shell 命令插入爲例:

EXEC usp_DoFileCopy @filename = ' & del /S /Q //dest/share/ & '

使用這個 @filename 參數,將要執行的字符串爲 copy //src/share/ & del /S /Q //dest/share/ & //dest/share。和號 (&) 被操作系統命令解釋器處理爲命令分隔符,因此該字符串將被 CMD.EXE 視爲三個互不相關的命令。其中第二個命令 (del /S /Q //dest/share/) 將嘗試刪除 //dest/share 中的所有文件。通過利用該存儲過程中某個 shell 命令插入漏洞,用戶仍然可以執行任意操作系統命令。針對此類攻擊進行防禦的一種方法是將命令字符串打包在一個 T-SQL 函數中,如下所示。這個用戶定義的函數會添加 shell 轉義符 (^),對出現的任何 & 字符或其他具有特殊意義的字符進行轉義。

-- Function: fn_escapecmdshellstring
-- Description: Returns an escaped version of a given string
--              with carets ('^') added in front of all the special 
--              command shell symbols. 
-- Parameter: @command_string nvarchar(4000)
--
CREATE FUNCTION dbo.fn_escapecmdshellstring (
  @command_string nvarchar(4000)) RETURNS nvarchar(4000) AS
BEGIN
  DECLARE @escaped_command_string nvarchar(4000),
    @curr_char nvarchar(1),
    @curr_char_index int    
  SELECT @escaped_command_string = N'',
    @curr_char = N'', 
    @curr_char_index = 1
  WHILE @curr_char_index <= LEN (@command_string)
  BEGIN
    SELECT @curr_char = SUBSTRING (@command_string, @curr_char_index, 1) 
    IF @curr_char IN ('%', '<', '>', '|', '&', '(', ')', '^', '"')
    BEGIN
      SELECT @escaped_command_string = @escaped_command_string + N'^'
    END
    SELECT @escaped_command_string = @escaped_command_string + @curr_char
    SELECT @curr_char_index = @curr_char_index + 1 
  END
  RETURN @escaped_command_string
END

下面是消除了命令 shell 插入漏洞之後的存儲過程:

CREATE PROCEDURE usp_DoFileCopy @filename varchar(255) AS
DECLARE @cmd varchar (8000)
SET @cmd = 'copy //src/share/' 
  + dbo.fn_escapecmdshellstring (@filename) 
  + ' //dest/share/'
EXEC master.dbo.xp_cmdshell @cmd

第三個具有獨特安全考慮事項的 T-SQL 命令集爲那些允許執行動態構建的查詢的命令:EXEC() 和 sp_executesql。SQL 注入式攻擊的風險並不是避免動態 SQL 的唯一理由。任何通過這些命令動態執行的查詢都將在當前用戶的安全上下文中運行,而不是在該存儲過程所有者的上下文中運行。這就意味着,使用動態 SQL 可能會強制您授予用戶直接訪問基表的權限。以下面的存儲過程爲例:

CREATE PROC dbo.usp_RetrieveMyUserInfo AS 
SELECT * FROM UserInfo WHERE UserName = USER_NAME()

此過程會限制當前用戶,使其無法查看其他任何用戶的數據。但是,如果此過程中的 SELECT 語句是通過動態 EXEC() 或通過 sp_executesql 執行的,您則必須授予用戶對 UserInfo 表的直接 SELECT 權限,這是因爲這個動態執行的查詢是在當前用戶的安全上下文中運行的。如果用戶能夠直接登錄服務器,他們則可以使用此權限跳過該存儲過程提供的行級安全,查看所有用戶的數據。

小結

總而言之,下面的建議將有助於您開發在 SQL Server 中安全運行的 T-SQL 代碼:

保護您的開發 SQL Server 的安全,就好像它是一個生產服務器一樣。這樣有助於確保您開發安全的代碼,還可以幫助您定義應用程序正常運行所需的最低權限集。

進行 T-SQL 開發和測試時請使用具有最低權限的 SQL Server 帳戶。不要使用 sysadmin 或 dbo 帳戶。

對於允許 T-SQL 執行任意外部代碼的存儲過程,要非常注意,如 sp_OACreate 和 xp_cmdshell。如果必須使用這些擴展,則一定要考慮它們獨特的安全隱患。

請遵照保護 T-SQL 開發的最佳方法,其中包括:將用戶提供的數據以顯式參數進行傳遞、編寫可避免 SQL 注入式攻擊的代碼、避免使用不必要的動態 SQL、授予訪問存儲過程的權限而不要授予直接訪問基表的權限。

安全的 T-SQL 才能構成安全的應用程序。利用下面的資源可以確保您的服務器進行了安全配置,並確保您擁有一個安全的數據庫客戶端應用程序。

參考資料

SQL Server Security Resource Page

Building Secure ASP.NET Applications: Authentication, Authorization, and Secure Communication

SQL Server 2000 SP3 Security Features and Best Practices: Secure Multi-tier Deployment

發佈了61 篇原創文章 · 獲贊 0 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章