構建安全的數據訪問
發佈日期: 1/10/2005 | 更新日期: 1/10/2005
查看全部的安全性指導主題
Microsoft Corporation
本單元概要
數據訪問是從 ASP.NET Web 應用程序使用幾個 ADO.NET 數據提供程序中的一個訪問數據庫的過程。
數據庫是應用程序級攻擊的主要目標。攻擊者使用應用程序級攻擊,以利用您的數據訪問代碼中的缺陷,獲取未授權的數據庫訪問權限。如果所有其他受攻擊區域都關閉了,則應用程序的前門 — 端口 80,將成為攻擊者竊取、操縱和破壞數據的路徑選擇。
本單元説明了如何構建安全的數據訪問代碼,並避免常見的缺陷和失誤。本單元提出了一系列對策和防範技術,可以用在數據訪問代碼中減輕與數據訪問有關的一些最大威脅。
返回頁首
目標
使用本單元可以:
|
•
|
設計、構建和部署安全的數據訪問代碼。 |
|
•
|
使用代碼訪問安全和基於角色的安全限制未授權的調用方或者代碼進行訪問。 |
|
•
|
安全地對用户進行身份驗證。 |
|
•
|
防止 SQL 注入攻擊。 |
|
•
|
保護數據庫連接字符串。 |
|
•
|
使用加密保護存儲在數據庫中的數據。 |
|
•
|
保護跨網絡發送到數據庫和來自數據庫的數據。 |
|
•
|
安全地將密碼(與帶 salt 值的散列值一起)存儲在數據庫中。 |
|
•
|
實現安全的異常處理。 |
|
•
|
瞭解如何使用代碼訪問安全以允許中度信任的 Web 應用程序使用 OLE DB、Oracle 和 ODBC 數據提供程序(這些數據提供程序要求完全信任)。 |
|
•
|
瞭解可用於應對常見的數據訪問威脅(包括 SQL 注入、配置數據的泄漏、敏感應用程序數據的泄漏、數據庫架構和連接詳細信息的泄漏、未授權的訪問和網絡偵聽)的對策。 |
返回頁首
適用範圍
本單元適用於下列產品和技術:
|
•
|
Microsoft® Windows® Server 2000 和 Windows Server™ 2003 操作系統 |
|
•
|
Microsoft .NET Framework 1.1 和 ASP.NET 1.1 |
|
•
|
Microsoft SQL Server™ |
返回頁首
如何使用本單元
要想充分利用本單元,請在本單元之前或者結合本單元閲讀以下單元:
|
•
|
閲讀“威脅與對策”單元。此單元能使您對 Web 應用程序所面臨的潛在威脅和對策有更廣和更深的理解。 |
|
•
|
閲讀“安全 Web 應用程序設計指南”單元。此單元中,您將瞭解構建安全解決方案的體系結構、設計挑戰和指導。 |
|
•
|
閲讀“保護數據庫服務器”單元。閲讀此單元可理解如何保護數據庫服務器。 |
|
•
|
閲讀“構建安全的程序集”單元。此單元中有關構建安全的程序集和開發安全託管代碼的指導和推薦實踐,應該也適用於數據訪問代碼。 |
|
•
|
使用評估單元。要在產品生存期的不同階段審查數據訪問的安全,請參考以下單元中的 Web 服務部分:“安全的體系結構和設計審查”、“安全代碼審查”和“安全部署審查”。 |
|
•
|
使用核對表。“核對表:保護數據訪問的安全”包括一個易於參考的核對錶。可以將這個基於任務的核對錶用作本單元中推薦實踐的一個總結。 |
請注意在當前版本的 .NET Framework (1.1) 中,只有 ADO.NET SQL Server 數據訪問提供程序支持部分信任調用方,可安全地用於部分信任 Web 應用程序。OLE DB、Oracle 和 ODBC ADO.NET 數據提供程序都要求完全信任。
本頁內容
|
本單元概要 |
|
目標 |
|
適用範圍 |
|
如何使用本單元 |
|
威脅與對策 |
|
設計注意事項 |
|
輸入驗證 |
|
SQL 注入 |
|
身份驗證 |
|
授權 |
|
配置管理 |
|
敏感數據 |
|
異常管理 |
|
構建安全的數據訪問組件 |
|
代碼訪問安全注意事項 |
|
部署注意事項 |
|
小結 |
|
其他資源 |
返回頁首
威脅與對策
要構建安全的數據訪問代碼,需要了解威脅是什麼,數據訪問代碼中是如何出現常見缺陷的,以及如何使用相應的對策降低風險。
數據訪問代碼的最大威脅有:
|
•
|
SQL 注入 |
|
•
|
配置數據的泄漏 |
|
•
|
敏感應用程序數據的泄漏 |
|
•
|
數據庫架構和連接詳細信息的泄漏 |
|
•
|
未授權訪問 |
|
•
|
網絡偵聽 |
圖 1 顯示了這些最大的威脅。
圖 1. 對數據訪問代碼的威脅和攻擊
SQL 注入
SQL 注入攻擊利用有缺陷的數據訪問代碼,使攻擊者可在數據庫中執行任意命令。如果應用程序使用在數據庫中無約束的帳户,威脅更大,因為這將賦予攻擊者更大的執行查詢和命令的自由。
缺陷
使您的數據訪問代碼易受 SQL 注入攻擊的常見缺陷包括:
|
•
|
脆弱的輸入驗證 |
|
•
|
不使用類型安全的參數動態構造 SQL 語句 |
|
•
|
過高特權的數據庫登錄的使用 |
對策
應對 SQL 注入攻擊,要確保:
|
•
|
約束和淨化輸入數據。 |
|
•
|
使用類型安全的 SQL 參數進行數據訪問。這些參數可用於存儲過程或者動態構造的 SQL 命令字符串。參數執行類型和長度檢查,並確保注入的代碼被視為文本數據,而不是可執行的數據庫語句。 |
|
•
|
使用有數據庫受限權限的帳户。理想情況下,您應該只給數據庫中經過選擇的那些存儲過程授予執行權限,而且不要提供直接的表訪問權限。 |
配置數據的泄漏
數據訪問代碼使用的最敏感的配置數據是數據庫連接字符串。如果有安全問題的連接字符串中包括用户名和密碼,結果將更嚴重。
缺陷
以下缺陷將增加與有安全問題的配置數據相關的安全風險:
|
•
|
使用 SQL 身份驗證,這要求在連接字符串中指定憑據 |
|
•
|
在代碼中嵌入連接字符串 |
|
•
|
配置文件中的明文連接字符串 |
|
•
|
未能加密連接字符串 |
對策
要防止配置數據的泄漏:
|
•
|
使用 Windows 身份驗證,這樣連接字符串中不會包含憑據。 |
|
•
|
加密連接字符串和限制訪問加密數據。 |
敏感應用程序數據的泄漏
許多應用程序存儲着敏感的數據,如客户的信用卡號碼。保護這類數據的私密性和完整性是非常重要的。
缺陷
會導致敏感應用程序數據泄漏的編碼實踐包括:
|
•
|
未加密存儲數據 |
|
•
|
脆弱的授權 |
|
•
|
脆弱的加密 |
對策
要防止敏感應用程序數據的泄漏:
|
•
|
使用堅固的加密機制保護數據。 |
|
•
|
在執行數據訪問之前授權每個調用方,這樣用户只能看到他們自己的數據。 |
數據庫架構和連接詳細信息的泄漏
如果您的代碼將異常詳細信息返回客户端,惡意用户就可使用這些信息攻擊服務器。數據訪問代碼中的異常會暴露敏感的信息,如數據庫架構的詳細信息、數據存儲區的性質和 SQL 代碼片段。
缺陷
以下缺陷可能導致信息泄漏:
|
•
|
異常處理不當 |
|
•
|
脆弱的 ASP.NET 配置,會使未處理的異常詳細信息返回客户端 |
對策
要防止這些泄漏問題:
|
•
|
在您的數據訪問代碼中捕獲、記錄和處理數據訪問異常。 |
|
•
|
返回一般性錯誤消息給調用方。這需要對 Web.config 或者 Machine.config 配置文件中的 <customErrors> 元素進行適當的配置。 |
未授權訪問
如果授權不當,用户可能能夠看到另一個用户的數據並可能能夠訪問其他受限的數據。
缺陷
可能允許未授權訪問的實踐包括:
|
•
|
數據訪問代碼中缺乏授權,會提供無限的訪問權限 |
|
•
|
特權過高的數據庫帳户 |
對策
為防止未授權的訪問:
|
•
|
使用主體權限要求授權調用方用户。 |
|
•
|
使用代碼訪問安全權限要求對調用方代碼授權。 |
|
•
|
使用受限權限限制應用程序登錄數據庫,並防止直接對錶進行訪問。 |
網絡偵聽
大多數應用程序的部署體系結構都包括一個將數據訪問代碼從數據庫服務器分隔開來的物理隔離層。因此,敏感的數據如特定於應用程序的數據或者數據庫登錄憑據,必須進行保護,以防網絡偵聽。
缺陷
以下實踐增加了網絡偵聽的可能:
|
•
|
SQL 身份驗證過程中在網絡上傳遞明文憑據 |
|
•
|
發送到數據庫服務器和來自數據庫服務器的未加密敏感應用程序數據 |
對策
為了限制網絡偵聽缺陷,應該:
|
•
|
使用 Windows 身份驗證避免在網絡上發送憑據。 |
|
•
|
在數據庫服務器上安裝服務器證書。這將使網絡上傳輸的 SQL 憑據自動加密。 |
|
•
|
在 Web 服務器和數據庫服務器之間使用 SSL 連接保護敏感的應用程序數據。這需要使用數據庫服務器證書。 |
|
•
|
在 Web 和數據庫服務器之間使用 IPSec 加密信道。 |
返回頁首
設計注意事項
在開始編寫代碼之前,有許多重要問題需要在設計時考慮。重要的注意事項有:
|
•
|
使用 Windows 身份驗證。 |
|
•
|
使用最低特權帳户。 |
|
•
|
使用存儲過程。 |
|
•
|
在存儲區中保護敏感的數據。 |
|
•
|
使用不同的數據訪問程序集。 |
使用 Windows 身份驗證
理想情況下,您的設計應該使用 Windows 身份驗證以獲得更多安全上的優勢。使用 Windows 身份驗證,就不必存儲帶有嵌入憑據的數據庫連接字符串,憑據也不會在網絡上傳遞,而且還可從安全的帳户和密碼管理策略中獲益。但是您的確需要仔細考慮應該使用哪個帳户通過 Windows 身份驗證連接 SQL Server。
有關更多信息,請參閲本單元后面的“身份驗證”。
使用最低特權帳户
您的應用程序應該使用最低特權帳户,該帳户只有數據庫的有限權限。確保應用程序登錄數據庫經過適當的授權和限制。有關詳細信息,請參閲本單元后面的“授權”。
如果您的帳户受到損壞或者注入了惡意代碼,使用最低特權帳户可減少風險,限制可能的損害。如果出現 SQL 注入,命令將執行在應用程序登錄所定義的安全上下文中,並受到登錄具有的數據庫相關權限的限制。如果您使用特權過高的帳户(例如,作為 SQL Server sysadmin 角色的成員)連接,攻擊者就可對服務器上的任何數據庫執行任何操作。這包括插入、更新和刪除數據、刪除表和執行操作系統命令。
重要説明 不要使用 sa 帳户或者任何屬於 SQL Server sysadmin 或者 db_owner 角色成員的帳户連接 SQL Server。
使用存儲過程
存儲過程在性能、可維護性和安全方面都有其優勢。應該儘可能地使用參數化的存儲過程進行數據訪問。其安全方面的優勢包括:
|
•
|
您可以限制應用程序進行數據庫登錄,使其只有執行指定存儲過程的權限。授予直接表訪問權限是不必要的。這有助於降低 SQL 注入攻擊帶來的風險。 |
|
•
|
對所有傳遞給存儲過程的輸入數據執行長度和類型檢查。而且,參數不能被視為可執行代碼。同樣,這也降低了 SQL 注入攻擊的風險。 |
如果您由於某種原因無法使用參數化的存儲過程,需要動態構造 SQL 語句,也應該使用類型化參數和參數佔位符確保對輸入數據進行長度和類型檢查。
保護存儲區中的敏感數據
確定存儲的哪些數據需要保證私密性和完整性。如果您在數據庫中存儲密碼只是為了驗證,可以考慮使用單向散列。即使密碼錶受到損壞,也無法使用散列值獲取明文密碼。
如果您需要存儲敏感的用户提供的數據如信用卡號碼,應該使用堅固的對稱加密算法(如三重 DES (3DES))加密數據。可以使用 Win32 數據保護 API (DPAPI) 加密 3DES 加密密鑰,並將加密的密鑰存儲在帶有受限的 ACL 的只有管理員和您的應用程序進程帳户可使用的註冊表項中。
為什麼不使用 DPAPI 呢?
雖然推薦將 DPAPI 用於對遇到機器故障時可手工進行還原和重構的連接字符串和其他密文(如帳户憑據)進行加密,但是它並不太適合存儲信用卡號碼之類的數據。這是因為存在可還原性問題(如果密鑰丟失,沒有辦法還原加密的數據)和 Web 服務器場問題。相反,您應該使用對稱加密算法(如 3DES),並使用 DPAPI 加密密鑰。
使 DPAPI 不太適合在數據庫中存儲敏感數據的主要問題可以總結如下:
|
•
|
如果 DPAPI 用於機器密鑰,您傳遞 CRYPTPROTECT_LOCAL_MACHINE 給 CryptProtectData 和 CryptUnprotectData 函數,機器帳户將生成加密密鑰。這意味着 Web 服務器場中每個服務器都有不同的密鑰,這防止了一個服務器能夠訪問另一個服務器加密的數據。同樣,如果 Web 服務器機器遭到破壞,密鑰丟失,則加密的數據將無法從數據庫還原。 |
|
•
|
如果您使用機器密鑰方式,則此計算機上的任何用户都可解密數據(除非您還使用其他加密機制)。 |
|
•
|
如果您將 DPAPI 用於用户密鑰,並使用本地用户帳户,每個 Web 服務器上的每個本地帳户都有不同的安全標識符 (SID),並生成不同的密鑰,這就防止了一個服務器能夠訪問另一個服務器加密的數據。 |
|
•
|
如果您將 DPAPI 用於用户密鑰,並在 Web 服務器場中使用跨機器的漫遊用户配置文件,則所有數據都將共享相同的加密/解密密鑰。但是,如果負責漫遊用户配置文件帳户的域控制器遭到破壞,將無法重新創建具有相同的 SID 的用户帳户,而且您無法從數據庫中還原加密數據。 同樣,使用漫遊用户配置文件,如果有人設法檢索數據,假如攻擊者能夠以特定的用户帳户運行代碼,則數據可在網絡中的任何機器上解密。這增加了潛在的受攻擊面,我們不推薦這樣做。 |
使用不同數據訪問程序集
如果您能夠選擇的話,不要將數據訪問邏輯直接放在 ASP.NET 頁或者代碼隱藏文件中。將數據訪問邏輯放在不同的程序集,並實現一個邏輯數據訪問層(獨立於應用程序業務邏輯和表示邏輯)有安全、重用和維護上的優點。
從安全角度來看,您可以:
|
•
|
對程序集使用強名稱,這能夠提供防篡改功能。 |
|
•
|
使用沙箱保護隔離您的數據訪問代碼,這在代碼需要支持部分信任調用方(例如,部分信任 Web 應用程序)時非常重要。 |
|
•
|
使用數據訪問方法和類,這些方法和類使用代碼標識權限要求對調用方代碼授權。 |
為了進行縱深防範,在您的業務組件中使用主體權限要求執行基於主體的授權,並使用代碼標識權限要求對調用您的數據訪問邏輯的代碼進行授權,如圖 2 中所示。
圖 2. 表示層、業務層和數據訪問層的分離
有關授權數據訪問代碼的更多信息,請參閲本單元后面的“授權”部分。
返回頁首
輸入驗證
除了確保您的數據庫保持有效和一致的數據的業務需求之外,您還必須在向數據庫提交數據之前對其進行驗證,以防止 SQL 注入。如果您的數據訪問代碼是從當前信任邊界內的其他組件收到輸入,而且已知數據已經進行了驗證(例如,通過 ASP.NET Web 頁或者業務組件進行驗證),那麼您的數據訪問代碼可以忽略進一步的數據驗證。但是,一定要在數據訪問代碼中使用 SQL 參數。這些參數將驗證輸入參數的類型和長度。下一部分將討論 SQL 參數的使用。
返回頁首
SQL 注入
SQL 注入攻擊在應用程序使用輸入構建動態的 SQL 語句以訪問數據庫時可能發生。SQL 注入攻擊還可能在代碼使用一些存儲過程(傳入的參數有包含未篩選的用户輸入)時發生。SQL 注入可能使攻擊者能夠使用應用程序登錄在數據庫中執行命令。如果應用程序使用特權過高的帳户連接數據庫,問題將更加嚴重。
注 傳統的安全措施,如 SSL 和 IPSec 的使用,並不會為您防範 SQL 注入攻擊。
防止 SQL 注入
可以使用以下對策防止 SQL 注入攻擊:
|
•
|
約束輸入。 |
|
•
|
使用類型安全的 SQL 參數。 |
約束輸入
驗證輸入的類型、長度、格式和範圍。如果輸入不可能是數值,那麼就不接受數值。考慮輸入從何而來。如果它來自已知執行了全面輸入驗證的可信來源,可以選擇在數據訪問代碼中忽略數據驗證。如果數據來自不可信的來源或者為了進行縱深防範,您的數據訪問方法和組件都應該驗證輸入。
使用類型安全的 SQL 參數
SQL 中的 Parameters 集合提供了類型檢查和長度驗證。如果您使用 Parameters 集合,輸入將被視為文本值處理,而且 SQL 不會將它視為可執行代碼。使用 Parameters 集合的另一個好處是您可以實施類型和長度檢查。如果值超出範圍將觸發異常。這是縱深防範的一個很好的例子。
重要説明 SSL 並不保護您免受 SQL 注入攻擊。任何訪問數據庫的應用程序,如果沒有正確的輸入驗證和適當的數據訪問技術,都容易遭到 SQL 注入攻擊。
儘可能地使用存儲過程,而且應該通過 Parameters 集合調用它們。
在存儲過程中使用參數集合
以下代碼片段説明了 Parameters 集合的使用:
SqlDataAdapter myCommand = new SqlDataAdapter("AuthorLogin", conn);
myCommand.SelectCommand.CommandType = CommandType.StoredProcedure;
SqlParameter parm = myCommand.SelectCommand.Parameters.Add(
"@au_id", SqlDbType.VarChar, 11);
parm.Value = Login.Text;
在這種情況下,@au_id 參數將被當作文本值而不是可執行代碼。同樣,對參數將進行類型和長度檢查。在上面的示例中,輸入值不能長於 11 個字符。如果數據不遵守參數所定義的類型或者長度,將出現異常。
請注意使用存儲過程並不一定防止 SQL 注入。重要的是在存儲過程中使用參數。如果不使用參數,您的存儲過程如果使用未經篩選的輸入時,就很容易遭到 SQL 注入攻擊。例如,以下代碼片段就存在問題:
SqlDataAdapter myCommand = new SqlDataAdapter("LoginStoredProcedure '" +
Login.Text + "'", conn);
重要説明 如果您使用存儲過程,一定要使用參數。
在動態 SQL 中使用參數集合
如果無法使用存儲過程,您還可以使用參數,如以下代碼片段中所示:
SqlDataAdapter myCommand = new SqlDataAdapter(
"SELECT au_lname, au_fname FROM Authors WHERE au_id = @au_id", conn);
SqlParameter parm = myCommand.SelectCommand.Parameters.Add("@au_id",
SqlDbType.VarChar, 11);
parm.Value = Login.Text;
使用參數批處理
有一個常見的誤解,如果您將幾個 SQL 語句串聯起來,在一次往返中發送一批語句給服務器,是無法使用參數的。但是,如果能肯定參數名稱不會重複,其實可以使用這個技術。可以通過在 SQL 文本串聯的過程中,為每個參數名稱添加一個數或者一些其他唯一值來輕易實現這一點。
使用篩選器例程
另一種用來防範 SQL 注入攻擊的方式是開發篩選器例程,在對 SQL 有特殊意義的字符中添加轉義符,如一個撇號字符。以下代碼片段説明了添加轉義符的篩選器例程:
private string SafeSqlLiteral(string inputSQL)
{
return inputSQL.Replace("'", "''");
}
例程會出現問題(如上面的這種問題)和不應完全依賴它們的原因在於,攻擊者可使用 ASCII 十六進制字符繞過您的檢查。但是,您還是應該對輸入進行篩選,這是縱深防範策略的一部分。
注 不要依賴對輸入的篩選。
請注意如果您使用 LIKE 子句,通配符仍然需要使用轉義符。以下代碼片段説明了這一技術:
s = s.Replace("[", "[[]");
s = s.Replace("%", "[%]");
s = s.Replace("_", "[_]");
返回頁首
身份驗證
當您的應用程序與 SQL Server 數據庫連接時,可以選擇 Windows 身份驗證或者 SQL 身份驗證。Windows 身份驗證安全性更高。如果您必須使用 SQL 身份驗證(這可能因為需要使用許多不同的帳户連接數據庫而且想避免調用 LogonUser),那麼應該採取更多步驟儘可能地降低額外的風險。
注 使用 LogonUser 創建模擬標記,需要 Microsoft Windows 2000 上強大的“Act as part of operating system”特權,因此應該避免使用這種方式。
考慮以下推薦實踐:
|
•
|
使用 Windows 身份驗證。 |
|
•
|
保護 SQL 身份驗證的憑據。 |
|
•
|
使用最低特權帳户連接。 |
使用 Windows 身份驗證
Windows 身份驗證不會跨網絡發送憑據。如果 Web 應用程序使用 Windows 身份驗證,在大多數情況下,應該使用服務帳户或者進程帳户(如 ASPNET 帳户)來連接數據庫。Windows 和 SQL Server 必須可識別在數據庫服務器上使用的帳户。帳户必須被授予登錄 SQL Server 的權限,而且登錄需要有訪問數據庫的相關權限。
使用 Windows 身份驗證時,應該使用可信的連接。以下代碼片段説明了使用 Windows 身份驗證的典型連接字符串。
以下示例使用了 SQL Server 的 ADO.NET 數據提供程序:
SqlConnection pubsConn = new SqlConnection(
"server=dbserver; database=pubs; Integrated Security=SSPI;");
以下示例使用了 OLE DB 數據源的 ADO.NET 數據提供程序:
OleDbConnection pubsConn = new OleDbConnection(
"Provider=SQLOLEDB; Data Source=dbserver; Integrated Security=SSPI;" +
"Initial Catalog=northwind");
保護 SQL 身份驗證的憑據
如果您必須使用 SQL 身份驗證,應確保憑據不會以明文形式跨網絡發送,而且應該加密數據庫連接字符串,因為其中包含憑據。
為了使 SQL Server 能夠自動加密跨網絡發送的憑據,在數據庫服務器上安裝服務器證書。此外,也可以使用 Web 服務器和數據庫服務器之間的 IPSec 加密信道保護所有發送到數據庫服務器和來自數據庫服務器的流量。要保護連接字符串,可以使用 DPAPI。有關更多信息,請參閲本單元后面的“配置管理”部分中的“保護連接字符串”。
使用最低特權帳户連接
您的應用程序應該通過使用最低特權帳户連接數據庫。如果您使用 Windows 身份驗證連接,從操作系統的角度來看,Windows 帳户應該具有最低特權,而且應該只有訪問 Windows 資源的受限特權和受限能力。此外,無論您是否使用 Windows 身份驗證或者 SQL 身份驗證,相應的 SQL Server 登錄都應該通過數據庫中的權限進行限制。
有關如何創建最低特權數據庫帳户和使用 Windows 身份驗證將 ASP.NET Web 應用程序與遠程數據庫連接的選項的更多信息,請參閲“保護 ASP.NET 應用程序的安全”單元中的“數據訪問”部分。
返回頁首
授權
授權過程確定了用户是否可檢索和操作特定的數據。授權有兩種方式:數據訪問代碼可使用授權確定是否執行所請求的操作,數據庫可執行授權限制應用程序使用的 SQL 登錄的能力。
若授權不當,用户可能能夠看到另一個用户的數據,而未授權的用户可能能夠訪問受限的數據。為了應對這些威脅應該:
|
•
|
限制未授權的調用方。 |
|
•
|
限制未授權的代碼。 |
|
•
|
在數據庫中限制應用程序。 |
圖 3 總結了應該使用的授權點和技術。
圖 3. 數據訪問授權、程序集和數據庫
請注意數據訪問代碼是如何使用權限要求對調用方用户或者調用方代碼進行授權的。代碼標識要求是 .NET 代碼訪問安全的一個功能。
要在數據庫中授權應用程序,使用最低特權 SQL 服務器登錄,該登錄帳户只有執行經過選擇的存儲過程的權限。除非有特殊原因,否則應用程序將無法得到直接對任何表執行創建、檢索、更新、破壞/刪除 (CRUD) 操作的授權。
注 存儲過程運行在數據庫系統的安全上下文之下。雖然可以通過授予特殊的存儲過程權限而約束應用程序的邏輯操作,但是您無法約束存儲過程所執行的操作的結果。存儲過程是可信代碼。存儲過程的接口必須使用數據庫權限進行保護。
限制未授權的調用方
代碼應該在用户連接數據庫之前根據角色或者標識對其授權。角色檢查通常用在應用程序的業務邏輯中,但是如果您沒有明確地區分業務和數據訪問邏輯,則應該在訪問數據庫的方法上使用主體權限要求。
以下屬性確保了只有是 Manager 角色成員的用户可調用 DisplayCustomerInfo 方法:
[PrincipalPermissionAttribute(SecurityAction.Demand, Role="Manager")]
public void DisplayCustomerInfo(int CustId)
{
}
如果您需要更細的授權粒度,並且需要在數據訪問方法中執行基於角色的邏輯,應該使用命令性的主體權限要求或者顯式的角色檢查,如以下代碼片段中所示:
using System.Security;
using System.Security.Permissions;
public void DisplayCustomerInfo(int CustId)
{
try
{
// Imperative principal permission role check to verify that the caller
// is a manager
PrincipalPermission principalPerm = new PrincipalPermission(
null, "Manager");
// Code that follows is only executed if the caller is a member
// of the "Manager" role
}
catch( SecurityException ex )
{
. . .
}
}
以下代碼片段使用一個顯式的、編程實現的角色檢查確保調用方是 Manager 角色的成員:
public void DisplayCustomerInfo(int CustId)
{
if(!Thread.CurrentPrincipal.IsInRole("Manager"))
{
. . .
}
}
限制未授權的代碼
通過使用 .NET Framework 代碼訪問安全 — 説得更具體一些就是代碼標識要求,您可以對可訪問數據訪問類和方法的程序集進行限制。
例如,如果您只想您的公司或者特定的開發單位編寫的代碼能夠使用您的數據訪問組件,應該使用一個 StrongNameIdentityPermission 並要求調用方程序集擁有指定公鑰的強名稱,如以下代碼片段中所示:
using System.Security.Permissions;
. . .
[StrongNameIdentityPermission(SecurityAction.LinkDemand,
PublicKey="002...4c6")]
public void GetCustomerInfo(int CustId)
{
}
要提取給定程序集公鑰的文本表示,可以使用以下命令:
sn -Tp assembly.dll
注 在 –Tp 開關中應該使用大寫的“T”。
因為 Web 應用程序程序集是動態編譯的,您無法使用這些程序集的強名稱。這將使限制只有特定的 Web 應用程序可使用數據訪問程序集非常困難。最佳方式是開發一個自定義權限並要求數據訪問組件的權限。完全信任 Web 應用程序(或者任何完全信任的代碼)可調用您的組件。而部分信任代碼只有在它被授予自定義權限時,可調用您的數據訪問組件。
自定義權限實現的示例,請參閲本指南“如何……”部分中的“如何創建自定義加密權限”。
在數據庫中限制應用程序
更好的方式是為 Windows 帳户創建一個 SQL Server 登錄供應用程序用來連接數據庫。然後將 SQL Server 登錄映射為數據庫中的數據庫用户。將數據庫用户置於用户定義的數據庫角色,並授予該角色權限。理想情況下,您應該只授予該角色應用程序所用的存儲過程的執行訪問權限。
有關如何配置這種方式的詳細信息,請參閲“保護 ASP.NET 應用程序的安全”單元中的“為您的 ASP.NET 應用程序配置數據訪問”。
返回頁首
配置管理
數據庫連接字符串是數據訪問代碼主要的配置管理關注點。應該仔細考慮將這些字符串存儲在哪裏,如何進行保護,特別在它們包含憑據的時候。要提高您的加密管理安全性應該:
|
•
|
使用 Windows 身份驗證。 |
|
•
|
保護您的連接字符串。 |
|
•
|
用受限的 ACL 保護 UDL 文件。 |
使用 Window 身份驗證
當您使用 Windows 身份驗證時,系統將為您管理憑據,而且憑據不會在網絡上傳遞。您還應該避免將用户名和密碼嵌入連接字符串中。
保護連接字符串
如果需要使用 SQL 身份驗證,則連接中將包含用户名和密碼。如果攻擊者利用 Web 服務器上的源代碼泄漏缺陷或者設法登錄服務器,他們將可檢索連接字符串。類似地,任何可合法登錄服務器的人都可查看它們。因此應該使用加密保護連接字符串。
加密連接字符串
通過使用 DPAPI 加密連接字符串。通過 DPAPI 加密,可避免加密密鑰管理問題,因為加密密鑰是由平台管理的,而且綁定於一個特定的計算機或者一個 Windows 用户帳户上。要使用 DPAPI,您必須通過 P/Invoke 調用 Win32 DPAPI 函數。
有關如何構建託管包裝類的詳細信息,請參閲“如何:創建 DPAPI 庫”,位於“Microsoft patterns & practices 第 I 卷,構建安全的 ASP.NET Web 應用程序:身份驗證、授權和安全通訊”的“如何……”部分中,網址是:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/secnetlpMSDN.asp。
安全地存儲加密的連接字符串
加密的連接字符串可以放在註冊表中或者 Web.config 或者 Machine.config 文件中。如果您使用 HKEY_LOCAL_MACHINE 下的註冊表項,可以對該項應用以下 ACL:
Administrators: Full Control
Process Account: Read
注 進程帳户是由數據訪問程序集在其中運行的進程所決定的。這通常是一個 ASP.NET 進程或者一個企業服務服務器進程(如果您的解決方案使用企業服務中間層的話)。
此外您可以考慮使用 HKEY_CURRENT_USER,這可提供受限的訪問。有關更多信息,請參閲“構建安全的程序集”單元中的“註冊表”部分。
注 如果您使用 Microsoft Visual Studio_ .NET 數據庫連接嚮導,連接字符串將以明文屬性值的形式存儲在 Web 應用程序代碼隱藏文件或者 Web.config 文件中。這兩種方式都應該避免。
雖然與使用受限的註冊表項相比安全性可能更差,但您還是可能需要將加密的字符串存儲在 Web.config 中以便更容易進行部署。在這種情況下,可以使用自定義的 <appSettings> 名稱-值對,如下所示:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="connectionString" value="AQA..bIE=" />
</appSettings>
<system.web>
...
</system.web>
</configuration>
要從 <appSettings> 元素訪問加密文本,應該使用 ConfigurationSettings 類,如下所示:
using System.Configuration;
private static string GetConnectionString()
{
return ConfigurationSettings.AppSettings["connectionString"];
}
不要將 Persist Security Info 設為‘True’或者‘Yes’
當您在連接字符串中包括 Persist Security Info 屬性時,將使 ConnectionString 屬性在返回給用户之前從連接字符串取得密碼。默認設置 false(等效於忽略 Persist Security Info 屬性)在連接數據庫後會將此信息丟棄。
用受限的 ACL 保護 UDL 文件
如果您的應用程序在 ADO.NET OLE DB 託管數據提供程序中使用外部通用數據鏈接 (UDL) 文件,應該使用 NTFS 權限限制訪問。使用以下受限的 ACL:
Administrators: Full Control
Process Account: Read
注 UDL 文件是沒有加密的。更安全的方法是使用 DPAPI 加密連接字符串並將其存儲在受限的註冊表項中。
返回頁首
敏感數據
許多 Web 應用程序在數據庫中存儲一種形式或另一種形式的敏感數據。如果攻擊者企圖對您的數據庫執行一個查詢,對任何敏感的數據項(如信用卡號碼)進行適當的加密是非常必要的。
|
•
|
如果您需要存儲敏感的數據,則應該對其進行加密。 |
|
•
|
保護跨網絡傳輸的敏感數據。 |
|
•
|
存儲帶 salt 值的密碼散列值。 |
如果您需要存儲敏感的數據,則應該對其進行加密
如果可能,儘量避免存儲敏感的數據。如果您必須存儲敏感的數據,應該加密該數據。
使用 3DES 加密
為了在數據庫中存儲敏感的數據(如信用卡號碼),應該使用一個堅固的對稱加密算法,如 3DES。
在開發期間,啓用 3DES 加密
|
1. |
使用 RNGCryptoServiceProvider 類生成一個堅固的(192 位,24 字節)加密密鑰。 |
|
2. |
備份加密密鑰,並將備份存儲在物理上非常安全的位置。 |
|
3. |
用 DPAPI 加密密鑰並將其存儲在一個註冊表項中。使用以下 ACL 保護註冊表項:
|
在運行時,在數據庫中存儲加密的數據
|
1. |
獲取要加密的數據。 |
|
2. |
從註冊表檢索加密的密鑰。 |
|
3. |
使用 DPAPI 解密密鑰。 |
|
4. |
使用帶有加密密鑰的 TripleDESCryptoServiceProvider 類加密數據。 |
|
5. |
在數據庫中存儲加密的數據。 |
在運行時,解密加密的密文
|
1. |
從數據庫檢索加密的數據。 |
|
2. |
從註冊表檢索加密的密鑰。 |
|
3. |
使用 DPAPI 解密密鑰。 |
|
4. |
使用 TripleDESCryptoServiceProvider 類解密數據。 |
通過此過程,如果用來加密密鑰的 DPAPI 帳户被破壞了,將從備份位置檢索 3DES 密鑰的備份,並在新帳户下使用 DPAPI 加密。新的加密密鑰可以存儲在註冊表中,而數據庫中的數據仍然可以解密。
有關創建託管 DPAPI 庫的更多信息,請參閲“如何:創建 DPAPI 庫”,位於“Microsoft patterns & practices Volume I, Building Secure ASP.NET Web Applications:Authentication, Authorization, and Secure Communication”中,網址是:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/secnetlpMSDN.asp。
保護跨網絡傳輸的敏感數據
通過網絡發送到數據庫服務器和來自數據庫服務器的敏感數據中,可能包含特定於應用程序的數據或者數據庫登錄憑據。為了確保網絡上數據的私密性和完整性,要麼使用平台級的解決方案(如服務器之間使用 IPSec 加密通信信道的安全數據中心所提供的),要麼配置應用程序建立 SSL 數據庫連接。後一種方式需要在數據庫服務器上安裝服務器證書。
有關使用 SSL 和 IPSec 的更多信息,請參閲“Microsoft patterns & practices 第 I 卷,構建安全的 ASP.NET Web 應用程序:身份驗證、授權和安全通訊”(網址是: )中“如何……”部分的“如何:在兩個服務器之間使用 IPSec 提供安全通信”和“如何:使用 SSL 保護與 SQL Server 2000 的通信”。
存儲帶 salt 值的密碼散列值
如果您需要實現包含用户名和密碼的用户存儲區,則不要以明文或者加密的格式存儲密碼。不要存儲密碼,而應該存儲帶附加 salt 的非可逆散列值以降低字典攻擊的風險。
注 salt 值是一個強加密的隨機數。
創建 salt 值
以下代碼説明了如何通過使用 System.Security.Cryptography 命名空間中的 RNGCryptoServiceProvider 類提供的隨機數生成功能生成 salt 值。
public static string CreateSalt(int size)
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buff = new byte[size];
rng.GetBytes(buff);
return Convert.ToBase64String(buff);
}
創建(帶 salt 值的)散列值
以下代碼片段説明了如何從提供的密碼和 salt 值生成散列值。
public static string CreatePasswordHash(string pwd, string salt)
{
string saltAndPwd = string.Concat(pwd, salt);
string hashedPwd =
FormsAuthentication.HashPasswordForStoringInConfigFile(
saltAndPwd, "SHA1");
return hashedPwd;
}
更多信息
有關實現存儲帶 salt 值的密碼散列值的用户存儲區的更多信息,請參閲“如何:結合使用窗體身份驗證與 SQL Server 2000”,位於“Microsoft patterns & practices 第 I 卷,構建安全的 ASP.NET Web 應用程序:身份驗證、授權和安全通訊”(網址是:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/secnetlpMSDN.asp)中的“如何……”部分。
返回頁首
異常管理
配置錯誤、代碼中的錯誤或者惡意輸入都會導致異常情況的出現。如果沒有適當的異常管理,這些情況會暴露有關位置和數據源性質以及重要的連接詳細信息等敏感信息。以下推薦實踐適用於數據訪問代碼:
|
•
|
捕獲和記錄 ADO.NET 異常。 |
|
•
|
確保數據庫連接總是關閉的。 |
|
•
|
在 ASP.NET 應用程序中使用一般性錯誤頁。 |
捕獲和記錄 ADO.NET 異常
將數據訪問代碼置於 try / catch 塊中並處理異常。在編寫 ADO.NET 數據訪問代碼時,ADO.NET 所生成的異常類型取決於數據提供程序。例如:
|
•
|
SQL Server .NET Framework 數據提供程序將生成 SqlExceptions。 |
|
•
|
OLE DB .NET Framework 數據提供程序將生成 OleDbExceptions。 |
|
•
|
ODBC .NET Framework 數據提供程序將生成 OdbcExceptions。 |
捕獲異常
以下代碼使用 SQL Server .NET Framework 數據提供程序,並説明了如何捕獲 SqlException 類型的異常。
try
{
// Data access code
}
catch (SqlException sqlex) // more specific
{
}
catch (Exception ex) // less specific
{
}
日誌記錄異常
您還應該將來自 SqlException 類的詳細信息記錄下來。這個類公開了包含異常情況詳細信息的屬性。這包括一個説明錯誤的 Message 屬性,一個唯一標識錯誤類型的 Number 屬性,和一個包含其他信息的 State 屬性。State 屬性通常用來指示特定錯誤情況的某次出現。例如,如果存儲過程在不止一行中出現了同樣的錯誤,State 屬性可指示特定的那一次。最後,Errors 集合包含可提供詳細 SQL Server 錯誤信息的 SqlError 對象。
以下代碼片段説明了如何通過使用 SQL Server .NET Framework 數據提供程序處理 SQL Server 錯誤情況:
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
// Method exposed by a Data Access Layer (DAL) Component
public string GetProductName( int ProductID )
{
SqlConnection conn = new SqlConnection(
"server=(local);Integrated Security=SSPI;database=products");
// Enclose all data access code within a try block
try
{
conn.Open();
SqlCommand cmd = new SqlCommand("LookupProductName", conn );
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@ProductID", ProductID );
SqlParameter paramPN =
cmd.Parameters.Add("@ProductName", SqlDbType.VarChar, 40 );
paramPN.Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
// The finally code is executed before the method returns
return paramPN.Value.ToString();
}
catch (SqlException sqlex)
{
// Handle data access exception condition
// Log specific exception details
LogException(sqlex);
// Wrap the current exception in a more relevant
// outer exception and re-throw the new exception
throw new Exception(
"Failed to retrieve product details for product ID: " +
ProductID.ToString(), sqlex );
}
finally
{
conn.Close(); // Ensures connection is closed
}
}
// Helper routine that logs SqlException details to the
// Application event log
private void LogException( SqlException sqlex )
{
EventLog el = new EventLog();
el.Source = "CustomAppLog";
string strMessage;
strMessage = "Exception Number : " + sqlex.Number +
"(" + sqlex.Message + ") has occurred";
el.WriteEntry( strMessage );
foreach (SqlError sqle in sqlex.Errors)
{
strMessage = "Message: " + sqle.Message +
" Number: " + sqle.Number +
" Procedure: " + sqle.Procedure +
" Server: " + sqle.Server +
" Source: " + sqle.Source +
" State: " + sqle.State +
" Severity: " + sqle.Class +
" LineNumber: " + sqle.LineNumber;
el.WriteEntry( strMessage );
}
}
確保數據庫連接總是關閉
如果出現了異常,關閉數據庫連接並釋放任何其他有限的資源是非常重要的。使用 finally 塊,或者 C# using 語句確保無論是否出現異常情況都將關閉連接。以上代碼已經説明了 finally 塊的使用。您還可以使用 C# using 語句,如下所示:
using ((SqlConnection conn = new SqlConnection(connString)))
{
conn.Open();
// Connection will be closed if an exception is generated or if control flow
// leaves the scope of the using statement normally
}
在 ASP.NET 應用程序中使用一般性錯誤頁
如果您的數據訪問代碼是由一個 ASP.NET Web 應用程序或者 Web 服務調用的,應該配置 <customErrors> 元素以防止異常詳細信息傳回最終用户。您還可以通過使用這個元素指定一般性錯誤頁,如下所示。
<customErrors mode="On" defaultRedirect="YourErrorPage.htm" />
為產品服務器設置 mode="On" 。當您在發佈前開發和測試軟件時,只能使用 mode="Off"。如果無法做到這一點,將導致返回給最終用户豐富的錯誤信息,如圖 4 中所示。這些信息可能包括數據庫服務器名稱、數據庫名稱和連接憑據。
圖 4. 詳細的異常信息會暴露敏感的數據
圖 4 還顯示了在導致異常的行附近的數據訪問代碼中仍然存在的許多缺陷。具體如下:
|
•
|
連接字符串是硬編碼的。 |
|
•
|
使用了高特權 sa 帳户來連接數據庫。 |
|
•
|
sa 帳户的密碼很脆弱。 |
|
•
|
SQL 命令構造很容易遭到 SQL 注入攻擊,輸入沒有進行驗證,而代碼沒有使用參數化的存儲過程。 |
返回頁首
構建安全的數據訪問組件
以下代碼給出了 CheckProductStockLevel 方法的一個示例實現,該方法可以用來查詢一個產品數據庫的庫存數量。這些代碼説明了本單元中前面介紹的許多重要的數據訪問代碼安全功能。
using System;
using System.Data;
using System.Data.SqlClient;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using Microsoft.Win32;
using DataProtection;
public static int CheckProductStockLevel(string productCode)
{
int quantity = 0;
// (1) Code protected by try/catch block
try
{
// (2) Input validated with regular expression
// Error messages should be retrieved from the resource assembly to help
// localization. The Localization code is omitted for the sake of brevity.
if (Regex.IsMatch(productCode, "^[A-Za-z0-9]{12}$") == false)
throw new ArgumentException("Invalid product code" );
//(3) The using statement ensures that the connection is closed
using (SqlConnection conn = new SqlConnection(GetConnectionString()))
{
// (4) Use of parameterized stored procedures is a countermeasure for
// SQL injection attacks
SqlCommand cmd = new SqlCommand("spCheckProduct", conn);
cmd.CommandType = CommandType.StoredProcedure;
// Parameters are type checked
SqlParameter parm =
cmd.Parameters.Add("@ProductCode",
SqlDbType.VarChar,12);
parm.Value = productCode;
// Define the output parameter
SqlParameter retparm = cmd.Parameters.Add("@quantity", SqlDbType.Int);
retparm.Direction = ParameterDirection.Output;
conn.Open();
cmd.ExecuteNonQuery();
quantity = (int)retparm.Value;
}
}
catch (SqlException sqlex)
{
// (5) Full exception details are logged. Generic (safe) error message
// is thrown back to the caller based on the SQL error code
// Log and error identification code has been omitted for clarity
throw new Exception("Error Processing Request");
}
catch (Exception ex)
{
// Log full exception details
throw new Exception("Error Processing Request");
}
return quantity;
}
// (6) Encrypted database connection string is held in the registry
private static string GetConnectionString()
{
// Retrieve the cipher text from the registry; the process account must be
// granted Read access by the key's ACL
string encryptedString = (string)Registry.LocalMachine.OpenSubKey(
@"Software\OrderProcessing\")
.GetValue("ConnectionString");
// Use the managed DPAPI helper library to decrypt the string
DataProtector dp = new DataProtector(DataProtector.Store.USE_MACHINE_STORE);
byte[] dataToDecrypt = Convert.FromBase64String(encryptedString);
return Encoding.ASCII.GetString(dp.Decrypt(dataToDecrypt,null));
}
上面給出的代碼説明了以下安全特徵(用註釋行的號碼標識)。
|
1. |
數據訪問代碼放在一個 try/catch 塊中。這對於防止在出現異常時將系統級信息返回給調用方至關重要。調用方 ASP.NET Web 應用程序或者 Web 服務可能會處理異常並將合適的一般性錯誤消息返回給客户端,但是數據訪問代碼並不依賴於此。 |
|
2. |
使用正則表達式驗證輸入。檢查了所提供的產品 ID,以驗證它只包含 A–Z 和 0–9 的字符,而且不超過 12 個字符。這種設計是用來防止 SQL 注入攻擊的第一個對策。 |
|
3. |
在 Microsoft Visual C#_ using 語句中創建了 SqlConnection 對象。這可確保無論是否發生異常,連接都會在方法中關閉。這將降低拒絕服務攻擊的威脅,這種攻擊試圖使用所有可用的數據庫連接。您可以通過使用 finally 塊得到類似的功能。 |
|
4. |
使用參數化的存儲過程進行數據訪問。這是另一個防止 SQL 注入攻擊的對策。 |
|
5. |
不將詳細的錯誤信息返回給客户端。對異常詳細信息進行記錄,以輔助問題的診斷。 |
|
6. |
加密的數據庫連接字符串存儲在註冊表中。最安全的存儲數據庫連接字符串的方式之一,是使用 DPAPI 加密字符串和將加密的密文存儲在一個受到保護的帶有受限 ACL 的註冊表項下。(例如,使用管理員:Full Control 和 ASP.NET 或者企業服務進程帳户:Read,這取決於哪個進程承載着組件。) 注 代碼説明了如何從註冊表中檢索連接字符串,然後使用託管的 DPAPI 輔助庫將其解密。這個庫是在“如何創建 DPAPI 庫”中提供的,該文章在“Microsoft patterns & practices 第 I 卷,構建安全的 ASP.NET Web 應用程序:身份驗證、授權和安全通訊”的“如何……”部分中。 |
返回頁首
代碼訪問安全注意事項
所有數據訪問都受代碼訪問安全權限要求的限制。選定的 ADO.NET 託管數據提供程序確定了準確的需求。以下表説明了必須為每個 ADO.NET 數據提供程序向數據訪問程序集授予的權限。
|
表 1 ADO.NET 數據提供程序要求的代碼訪問安全權限
|
|
|
ADO.NET提供方
|
必需的代碼訪問安全權限
|
|
SQL Server |
SqlClientPermission
|
|
OLE DB |
OleDbPermission* |
|
Oracle |
OraclePermission* |
|
ODBC |
OdbcPermission* |
*在本單元寫作時,.NET Framework 的 1.0和1.1 版上,OLE DB、Oracle 和 ODBC 提供程序只支持完全信任調用方。要從部分信任 Web 應用程序使用這些提供程序,您必須用沙箱保護數據訪問的安全代碼,這需要有專用的數據訪問程序集。有關説明如何用沙箱保護數據訪問的安全代碼和從中度信任的 Web 應用程序使用 OLE DB 數據提供程序的例子,請參閲“在 ASP.NET 中使用代碼訪問安全”單元。
如果您使用 ADO.NET SQL Server 數據提供程序,必須通過代碼訪問安全策略授予代碼 SqlClientPermission。完全和中度信任 Web 應用程序都具有這個權限。
是否代碼被授予了 SqlClientPermission 將決定代碼是否可連接 SQL Server。您還可以使用權限對數據庫連接字符串的使用進行限制。例如,您可以強制應用程序使用集成安全,也可以確保如果使用 SQL Server 安全,就不接受空白密碼。如果違反通過 SqlClientPermission 指定的規則,將引起運行時安全異常。
有關如何使用 SqlClientPermission 約束數據訪問的更多信息,請參閲“代碼訪問安全實踐”單元中的“數據訪問”部分。
返回頁首
部署注意事項
經過安全設計和開發的數據訪問組件如果沒有以安全的方式進行部署,仍然會存在缺陷,遭到攻擊。常見的部署實踐是將數據訪問代碼和數據庫置於不同服務器上。不同服務器經常是用內部防火牆分隔的,從而帶來了更多部署方面的注意事項。對於開發人員和管理員,應該注意以下問題:
|
•
|
防火牆限制 |
|
•
|
連接字符串管理 |
|
•
|
登錄帳户配置 |
|
•
|
登錄審核 |
|
•
|
網絡數據的私密性和完整性 |
防火牆限制
如果您通過防火牆連接 SQL Server,應該配置防火牆、客户端和服務器。通過使用 SQL Server 客户端網絡實用工具配置客户端,通過使用服務器網絡實用工具配置數據庫服務器。默認時,SQL Server 將偵聽 TCP 端口 1433,雖然這也可以更改。您必須在防火牆上打開選擇的端口。
根據您選擇的 SQL Server 的身份驗證模式以及應用程序是否使用分佈式事務,可能需要在防火牆上打開另外幾個端口:
|
•
|
如果應用程序使用 Windows 身份驗證連接 SQL Server,必須打開支持 Kerberos 或者 NTLM 身份驗證的端口。 對於不使用 Active Directory 的網絡,TCP 端口 139 通常對於 Windows 身份驗證是必需的。有關端口需求的更多信息,請參閲 TechNet 文章“TCP and UDP Port Assignments,”,網址是:http://www.microsoft.com/technet/prodtechnol/windows2000serv/reskit/tcpip/part4/tcpappc.asp,和“Security Considerations for Administrative Authority,”,網址是:http://www.microsoft.com/technet/security/bestprac/bpent/sec2/seconaa.asp |
|
•
|
如果您的應用程序使用分佈式事務(例如自動化 COM+ 事務),可能還需要配置防火牆允許 DTC 流量,以在不同的 DTC 實例之間、DTC 和資源管理器(如 SQL Server)之間傳遞數據。 |
有關完整的配置詳細信息,請參閲“保護數據庫服務器”單元中的“端口”部分。
連接字符串管理
許多應用程序主要出於性能原因,將連接字符串存儲在代碼中。但是,性能上的優勢是可以忽略的,而且使用文件系統緩存有助於確保將連接字符串存儲在外部文件中,這可提供差不多的性能。使用外部文件存儲連接字符串對於系統管理而言也是非常好的方式。
為了提高安全性,推薦的方式是使用 DPAPI 加密連接字符串。如果您的連接字符串包含用户名和密碼,這將尤其重要。然後,決定在哪裏存儲加密的字符串。註冊表是一個安全的位置,尤其在您使用 HKEY_CURRENT_USER 的時候,因為訪問僅限於在相關用户帳户下運行的進程。一個更便於部署的替代方案是將加密的字符串存儲在 Web.config 文件中。這兩種方式在本單元中前面的“配置管理”部分進行了討論。
登錄帳户的配置
應用程序使用一個最低特權帳户連接數據庫,這是非常重要的。這是降低 SQL 注入攻擊威脅的主要技術之一。
作為一個開發人員,您必須與數據庫管理員溝通應用程序登錄需要訪問的準確存儲過程和(可能的)表。理想情況下,您應該只允許應用程序登錄擁有與應用程序一起部署的一組受限存儲過程的執行權限。
對 SQL 或者 Windows 帳户或者應用程序用來連接數據庫的帳户使用堅固的密碼。
請參閲本單元前面“授權”部分中對數據庫中應用程序帳户的推薦授權策略。
登錄審核
應該配置 SQL Server,記錄失敗的登錄嘗試,以及可能的成功登錄嘗試。審核失敗登錄嘗試,對於檢測嘗試破解帳户密碼的攻擊者非常有用。
有關如何配置 SQL Server 審核的更多信息,請參閲“保護數據庫服務器”單元。
網絡數據的私密性和完整性
如果您使用 SQL 身份驗證連接 SQL Server,應該確保登錄憑據不會在網絡上公開。或者在數據庫服務器上安裝證書(這將使 SQL Server 加密憑據),或者使用 IPSec 加密信道連接數據庫。
推薦使用 IPSec 或者 SSL 連接數據庫,以保護髮送到數據庫和來自數據庫的敏感應用程序級數據。有關更多信息,請參閲“保護數據庫服務器”單元。
返回頁首
小結
本單元説明了對數據訪問代碼的主要威脅,並重點論述了常見的缺陷。SQL 注入是應該留意的主要威脅之一。除非您使用本單元中討論的正確對策,否則攻擊者將可利用您的數據訪問代碼在數據庫中運行任意命令。傳統的安全措施(如防火牆和 SSL)無法提供對 SQL 注入攻擊的防範。您應該徹底地驗證輸入並使用參數化的存儲過程作為最低限度的防範措施。
返回頁首
其他資源
有關更多信息,請參閲下列資源:
|
•
|
有關核對表的打印稿,請參閲“核對表:保護數據訪問的安全”。 |
|
•
|
有關保護開發人員工作站的信息,請參閲本指南中“如何……”部分的“如何保護開發人員工作站的安全”。 |
|
•
|
有關在 SQL Server 中使用 SSL 的信息,請參閲“如何:使用 SSL 保護與 SQL Server 2000 的通信”,位於“Microsoft patterns & practices 第 I 卷,構建安全的 ASP.NET Web 應用程序:身份驗證、授權和安全通訊”中的“如何……”部分中,網址是:http://msdn.microsoft.com/library/en-us/dnnetsec/html/SecNetHT19.asp。 |
|
•
|
有關使用IPSec 的信息,請參閲“如何:使用 IPSec 提供兩個服務器之間的安全通信”,位於“Microsoft patterns & practices 第 I 卷,構建安全的 ASP.NET Web 應用程序:身份驗證、授權和安全通訊”中的“如何……”部分中,網址是:http://msdn.microsoft.com/library/en-us/dnnetsec/html/SecNetHT18.asp。 |
|
•
|
有關使用 DPAPI 的信息,請參閲“如何:創建 DPAPI 庫”,位於“Microsoft patterns & practices 第 I 卷,構建安全的 ASP.NET Web 應用程序:身份驗證、授權和安全通訊”中的“如何……”部分中,網址是:http://msdn.microsoft.com/library/en-us/dnnetsec/html/SecNetHT07.asp。 |