SQL Server 2000的安全策略(二)

kitesky發表於2005-04-14

8、以最低許可權帳戶身份進行開發

通常情況下,保護開發伺服器安全的最佳方法,就好似它正在生產環境中執行那樣對它進行保護。您離這個目標越接近,那麼就可以越自信於您開發的程式碼可以在一個安全的生產環境中正常執行。
    在開發過程中,大家都著迷於使用具有 sysadmin 或 dbo SQL Server 許可權的帳戶,直到部署之前才轉換為一個許可權更低的帳戶。使用這種方法存在著一個問題:將設計人員的許可權集還原為最低的所需許可權集與在開發應用程式過程中編寫這些許可權集相比,前者要困難得多。
         鑑於部署應用程式之前您要決定可以取消哪些許可權,所以請不要使用 SQL sysadmin 帳戶開發 T-SQL 程式碼。如果使用 SQL sysadmin 帳戶,可能會造成這樣的結果,即應用程式會以比所需許可權更多的特權帳戶執行。因此,開發時請改為使用具有最低許可權的帳戶。
         使用這樣的帳戶進行開發時,您會逐漸地升高授予的特定許可權,以 EXEC(執行)一些必需的儲存過程、從某些表進行 SELECT(選擇)等。請編寫這些 GRANT 語句,以便可以將同樣的最低許可權輕鬆部署到生產環境中,而不會出現任何基於猜測的操作。
         這種理念同樣適用於測試。執行臨時測試以及結構更加複雜的測試時,所使用帳戶擁有的許可權集和使用者許可權應該與在生產環境中所使用帳戶擁有的許可權集和使用者許可權完全相同。
在開發過程中使用最低許可權帳戶的另一個優點在於,您可以避免不小心編寫出需要危險許可權或過高許可權的程式碼。例如,假設您需要在 T-SQL 中與第三方 COM 元件進行互動。為此,一種方法是傳送一個 SQL 批處理命令,它直接呼叫 sp_OACreate 和 sp_OAMethod 來操縱該 COM 物件。在應用程式使用 sysadmin 帳戶連線 SQL Server 的開發環境中,上述方法效果很好。但是,當您嘗試將已經開發完成的應用程式準備用於生產部署時,您就會發現如果使用許可權較低的帳戶,那麼該方法不會奏效。為了讓該應用程式能夠使用非 sysadmin 帳戶在生產環境中正常執行,您必須針對 sp_OACreate 顯式授予 EXECUTE 許可權。請考慮一下,如果某個使用者最終找到了一個方法,可以使用該應用程式登入執行任意程式碼,並利用此許可權針對 SQL Server 例項化一個類似Scripting.FileSystemObject 的 COM 物件,將會產生怎樣的安全隱患?

9T-SQL指令碼安全

有一些 T-SQL 命令和擴充套件儲存過程,它們具有自己獨特的安全考慮事項。

1sp_OACreate 及其相關的系統過程系列

例如 sp_OAMethodsp_OAGetProperty 等。透過授予應用程式登入直接訪問這些過程的許可權,會帶來該安全問題。為了避免此問題的發生,請絕對不要編寫直接呼叫 sp_OA 過程的應用程式程式碼,而要將對這些過程的所有引用都打包在您自己的 T-SQL 儲存過程中,並只授予訪問這些包裝儲存過程的許可權。另外,請不要允許應用程式程式碼將 COM 物件或方法的名稱作為可由包裝過程無條件呼叫的字串進行傳遞。

2 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 srcshare' + @filename + ' destshare'
        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 destshare & '

使用這個 @filename 引數,將要執行的字串為 copy srcshare & del /S /Q destshare & destshare和號 (&) 被作業系統命令直譯器處理為命令分隔符,因此該字串將被 CMD.EXE 視為三個互不相關的命令。其中第二個命令 (del /S /Q destshare) 將嘗試刪除 destshare 中的所有檔案。透過利用該儲存過程中某個 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  
      + dbo.fn_escapecmdshellstring (@filename) 
      + '
    EXEC master.dbo.xp_cmdshell @cmd

3)允許執行動態構建的查詢的命令

EXEC() sp_executesql。轉義對資料庫引擎可能具有特殊意義的字元序列。在 T-SQL 中,注入式攻擊中最常用的兩個字串為單引號字元 (') 和註釋字元序列 (--) SQL 注入式攻擊的風險並不是避免動態 SQL 的唯一理由。任何透過這些命令動態執行的查詢都將在當前使用者的安全上下文中執行,而不是在該儲存過程所有者的上下文中執行。這就意味著,使用動態 SQL 可能會強制您授予使用者直接訪問基表的許可權。以下面的儲存過程為例:

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

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

小結

(1)總而言之,下面的建議將有助於您開發在 SQL Server 中安全執行的 T-SQL 程式碼:

保護您的開發 SQL Server 的安全,就好像它是一個生產伺服器一樣。這樣有助於確保您開發安全的程式碼,還可以幫助您定義應用程式正常執行所需的最低許可權集。
進行 T-SQL 開發和測試時請使用具有最低許可權的 SQL Server 帳戶。不要使用 sysadmin dbo 帳戶。
對於允許 T-SQL 執行任意外部程式碼的儲存過程,要非常注意,如 sp_OACreate xp_cmdshell。如果必須使用這些擴充套件,則一定要考慮它們獨特的安全隱患。
請遵照保護 T-SQL 開發的最佳方法,其中包括:將使用者提供的資料以顯式引數進行傳遞、編寫可避免 SQL 注入式攻擊的程式碼、避免使用不必要的動態 SQL、授予訪問儲存過程的許可權而不要授予直接訪問基表的許可權。
安全的 T-SQL 才能構成安全的應用程式。利用下面的資源可以確保您的伺服器進行了安全配置,並確保您擁有一個安全的資料庫客戶端應用程式。
 

(2)去掉當前使用者以下許可權:

名稱預設最低角色
xp_cmdshell
sysadmin 固定伺服器角色的成員
  
Sp_OACreatesysadmin 固定伺服器角色的成員
Sp_OADestroysysadmin 固定伺服器角色的成員
Sp_OAGetErrorInfosysadmin 固定伺服器角色的成員
Sp_OAGetProperty sysadmin 固定伺服器角色的成員
Sp_OAMethodsysadmin 固定伺服器角色的成員
Sp_OASetPropertysysadmin 固定伺服器角色的成員
Sp_OAStopsysadmin 固定伺服器角色的成員
  
EXEC()使用者上下文
sp_executesql使用者上下文
  
xp_readmail
sysadmin 固定伺服器角色成員 以及master資料庫中的db_owner固定資料庫角色成員
  
Xp_regaddmultistring  
Xp_regdeletekey  
Xp_regdeletevalue  
Xp_regenumvalues  
Xp_regreadPublic資料庫角色成員
Xp_regremovemultistring  
Xp_regwrite 
  
sp_makewebtask  

--指令碼如下:
USE MASTER
GO

DECLARE @curr_user VARCHAR(128), @access_ctrl_sql VARCHAR(128)

SET @curr_user = USER_NAME()

-- 1. xp_cmdshell
SET @access_ctrl_sql = 'DENY EXECUTE ON xp_cmdshell TO ' + @curr_user
EXEC(@access_ctrl_sql)
IF @@ERROR <> 0
BEGIN
 GOTO ERROR_HANDLE
END

-- 2. Sp_OACreate
SET @access_ctrl_sql = 'DENY EXECUTE ON Sp_OACreate TO ' + @curr_user
EXEC(@access_ctrl_sql)
IF @@ERROR <> 0
BEGIN
 GOTO ERROR_HANDLE
END

-- 3. Sp_OADestroy
SET @access_ctrl_sql = 'DENY EXECUTE ON Sp_OADestroy TO ' + @curr_user
EXEC(@access_ctrl_sql)
IF @@ERROR <> 0
BEGIN
 GOTO ERROR_HANDLE
END

-- 4. Sp_OAGetErrorInfo
SET @access_ctrl_sql = 'DENY EXECUTE ON Sp_OAGetErrorInfo TO ' + @curr_user
EXEC(@access_ctrl_sql)
IF @@ERROR <> 0
BEGIN
 GOTO ERROR_HANDLE
END

-- 5. Sp_OAGetProperty
SET @access_ctrl_sql = 'DENY EXECUTE ON Sp_OAGetProperty  TO ' + @curr_user
EXEC(@access_ctrl_sql)
IF @@ERROR <> 0
BEGIN
 GOTO ERROR_HANDLE
END

-- 6. Sp_OAMethod
SET @access_ctrl_sql = 'DENY EXECUTE ON Sp_OAMethod TO ' + @curr_user
EXEC(@access_ctrl_sql)
IF @@ERROR <> 0
BEGIN
 GOTO ERROR_HANDLE
END

-- 7. Sp_OASetProperty
SET @access_ctrl_sql = 'DENY EXECUTE ON Sp_OASetProperty TO ' + @curr_user
EXEC(@access_ctrl_sql)
IF @@ERROR <> 0
BEGIN
 GOTO ERROR_HANDLE
END

-- 8. Sp_OAStop
SET @access_ctrl_sql = 'DENY EXECUTE ON Sp_OAStop TO ' + @curr_user
EXEC(@access_ctrl_sql)
IF @@ERROR <> 0
BEGIN
 GOTO ERROR_HANDLE
END

-- 9. xp_readmail
SET @access_ctrl_sql = 'DENY EXECUTE ON xp_readmail TO ' + @curr_user
EXEC(@access_ctrl_sql)
IF @@ERROR <> 0
BEGIN
 GOTO ERROR_HANDLE
END

-- 10. xp_regread
SET @access_ctrl_sql = 'DENY EXECUTE ON xp_regread TO ' + @curr_user
EXEC(@access_ctrl_sql)
IF @@ERROR <> 0
BEGIN
 GOTO ERROR_HANDLE
END

-- 11. sp_makewebtask
SET @access_ctrl_sql = 'DENY EXECUTE ON sp_makewebtask TO ' + @curr_user
EXEC(@access_ctrl_sql)
IF @@ERROR <> 0
BEGIN
 GOTO ERROR_HANDLE
END

SUCCESS_HANDLE:
BEGIN 
 print '為使用者 ' + @CURR_USER + ' 收回重要的儲存過程執行許可權成功'
 RETURN
END

ERROR_HANDLE:
BEGIN
 print '為使用者 ' + @CURR_USER + ' 收回重要的儲存過程執行許可權失敗'
 RETURN
END

GO

[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/66009/viewspace-796001/,如需轉載,請註明出處,否則將追究法律責任。

相關文章