本模塊內(nèi)容
數(shù)據(jù)訪問是使用幾個(gè)可用的 ADO.NET 數(shù)據(jù)提供程序之一從 ASP.NET Web 應(yīng)用程序訪問數(shù)據(jù)庫的過程。
此數(shù)據(jù)庫是應(yīng)用程序級攻擊的主要目標(biāo)。應(yīng)用程序級攻擊用于利用數(shù)據(jù)訪問代碼中的漏洞并獲取對數(shù)據(jù)庫未授權(quán)的訪問。如果所有其他攻擊方法都已失效,應(yīng)用程序的前門(即端口 8)將變成攻擊者竊取、操縱和破壞數(shù)據(jù)的可選路徑。
本模塊說明如何構(gòu)建安全的數(shù)據(jù)訪問代碼,以及如何避免常見的漏洞和缺陷。本模塊提供了一系列對策和防御技術(shù),您可以在自己的數(shù)據(jù)訪問代碼中使用它們來減少與數(shù)據(jù)訪問有關(guān)的主要威脅。
目標(biāo)
使用本模塊可以實(shí)現(xiàn):
? |
設(shè)計(jì)、構(gòu)建和部署安全的數(shù)據(jù)訪問代碼。
|
? |
使用代碼訪問安全性和基于角色的安全性可以限制未經(jīng)授權(quán)的調(diào)用方或代碼的訪問。
|
? |
安全地驗(yàn)證用戶的身份。
|
? |
防止 SQL 注入攻擊。
|
? |
確保數(shù)據(jù)庫連接字符串的安全。
|
? |
使用加密機(jī)制來保護(hù)存儲(chǔ)在數(shù)據(jù)庫中的數(shù)據(jù)。
|
? |
確保通過網(wǎng)絡(luò)發(fā)送到數(shù)據(jù)庫以及從數(shù)據(jù)庫發(fā)送的數(shù)據(jù)的安全。
|
? |
使用帶有salt 的哈希值將密碼安全地存儲(chǔ)在數(shù)據(jù)庫中。
|
? |
實(shí)現(xiàn)安全的異常處理。
|
? |
了解如何使用代碼訪問安全性,允許中等信任 Web 應(yīng)用程序使用 OLE DB、Oracle 和 ODBC 數(shù)據(jù)提供程序(這些提供程序需要完全信任)。
|
? |
了解應(yīng)使用哪些對策來解決常見的數(shù)據(jù)訪問威脅,包括 SQL 注入、配置數(shù)據(jù)泄露、敏感的應(yīng)用程序數(shù)據(jù)泄露、數(shù)據(jù)庫架構(gòu)和連接詳細(xì)信息的泄露、未授權(quán)的訪問和網(wǎng)絡(luò)竊聽。
|
適用范圍
本模塊適用于下列產(chǎn)品和技術(shù):
? |
Microsoft? Windows? 2000 Server 和 Microsoft Windows Server? 2003
|
? |
Microsoft .NET Framework 1.1 和 ASP.NET 1.1
|
? |
Microsoft SQL Server?
|
如何使用本模塊
為了充分理解本模塊內(nèi)容,請先閱讀下列模塊或與本模塊結(jié)合起來閱讀:
? |
閱讀模塊 2 威脅與對策。這將使您更廣泛深入地了解 Web 應(yīng)用程序所面臨的潛在威脅及其對策。
|
? |
閱讀模塊 4 Web 應(yīng)用程序安全設(shè)計(jì)指南。在本模塊中,您將了解構(gòu)建安全解決方案時(shí)所面臨的體系結(jié)構(gòu)和設(shè)計(jì)挑戰(zhàn),以及構(gòu)建準(zhǔn)則。
|
? |
閱讀模塊 18 保證數(shù)據(jù)庫服務(wù)器的安全。閱讀模塊 18 可了解如何確保數(shù)據(jù)庫服務(wù)器的安全。
|
? |
閱讀模塊 7 構(gòu)建安全的程序集。模塊 7 中構(gòu)建安全程序集和開發(fā)安全托管代碼的準(zhǔn)則和建議同樣應(yīng)該適用于數(shù)據(jù)訪問代碼
|
? |
使用評估模塊。要在產(chǎn)品周期的不同階段檢查數(shù)據(jù)訪問的安全性,請參見下列模塊中的“Web 服務(wù)”部分:模塊 5 安全性體系結(jié)構(gòu)和設(shè)計(jì)審查、模塊 21 代碼審查以及模塊 22 部署審查。
|
? |
使用檢查表。本指南“檢查表”部分中的檢查表:保護(hù)數(shù)據(jù)訪問包括一個(gè)便于參考的檢查表。可以將此基于任務(wù)的檢查表用作本模塊中各個(gè)建議的摘要。
|
請注意,在當(dāng)前版本的 .NET Framework (1.1) 中,只有 ADO.NET SQL Server 數(shù)據(jù)訪問提供程序才支持部分信任調(diào)用方,并且可以安全地用在部分信任 Web 應(yīng)用程序中。OLE DB、Oracle 和 ODBC ADO.NET 數(shù)據(jù)提供程序需要完全信任。
威脅與對策
要構(gòu)建安全的數(shù)據(jù)訪問代碼,需要了解數(shù)據(jù)訪問代碼中的威脅是什么、常見漏洞是如何產(chǎn)生的以及如何使用適當(dāng)?shù)膶Σ邅斫档惋L(fēng)險(xiǎn)。
數(shù)據(jù)訪問代碼面臨的主要威脅包括:
? |
SQL 注入
|
? |
配置數(shù)據(jù)的泄漏
|
? |
敏感應(yīng)用程序數(shù)據(jù)的泄漏
|
? |
數(shù)據(jù)庫架構(gòu)和連接詳細(xì)信息的泄露
|
? |
未授權(quán)的訪問
|
? |
網(wǎng)絡(luò)竊聽
|
圖 14.1 闡明了這些主要威脅。
圖 14.1
數(shù)據(jù)訪問代碼面臨的威脅和攻擊
SQL 注入
SQL 注入攻擊利用易受攻擊的數(shù)據(jù)訪問代碼,并允許攻擊者在數(shù)據(jù)庫中執(zhí)行任意命令。如果應(yīng)用程序使用數(shù)據(jù)庫中不受限制的帳戶,由于攻擊者可以更自由地執(zhí)行查詢和命令,因此受到的威脅會(huì)更大。
漏洞
使數(shù)據(jù)訪問代碼容易受到 SQL 注入攻擊的常見漏洞包括:
? |
弱輸入驗(yàn)證
|
? |
在不使用類型安全的參數(shù)時(shí)動(dòng)態(tài)構(gòu)造 SQL 語句
|
? |
使用特權(quán)過高的數(shù)據(jù)庫登錄
|
對策
要應(yīng)對 SQL 注入攻擊,請務(wù)必:
? |
限制和凈化輸入數(shù)據(jù)。
|
? |
使用類型安全的 SQL 參數(shù)進(jìn)行數(shù)據(jù)訪問。這些參數(shù)可以與存儲(chǔ)過程一起使用,也可以是動(dòng)態(tài)構(gòu)造的 SQL 命令字符串。參數(shù)執(zhí)行類型和長度檢查,并同時(shí)確保注入數(shù)據(jù)庫中的代碼被視為文本數(shù)據(jù)(而非可執(zhí)行語句)。
|
? |
使用在數(shù)據(jù)庫中具有有限權(quán)限的帳戶。理想情況下,只應(yīng)向數(shù)據(jù)庫中的選定存儲(chǔ)過程授予執(zhí)行權(quán)限,且不提供直接的表格訪問權(quán)限。
|
配置數(shù)據(jù)泄露
數(shù)據(jù)訪問代碼所使用的最敏感的配置數(shù)據(jù)是數(shù)據(jù)庫連接字符串。如果泄漏的連接字符串包括用戶名和密碼,后果將不堪設(shè)想。
漏洞
下列漏洞會(huì)增加與泄漏的配置數(shù)據(jù)相關(guān)的安全風(fēng)險(xiǎn):
? |
使用 SQL 身份驗(yàn)證,這要求在連接字符串中指定憑據(jù)
|
? |
代碼中嵌入的連接字符串
|
? |
配置文件中的明文連接字符串
|
? |
無法加密連接字符串
|
對策
要防止配置數(shù)據(jù)的泄漏:
? |
使用 Windows 身份驗(yàn)證,以便連接字符串中不包含憑據(jù)。
|
? |
加密連接字符串,并限制對已加密數(shù)據(jù)的訪問。
|
敏感應(yīng)用程序數(shù)據(jù)的泄漏
許多應(yīng)用程序都存儲(chǔ)敏感的數(shù)據(jù)(如客戶的信用卡號),一定要保護(hù)此類數(shù)據(jù)的私密性和完整性。
漏洞
下列編碼做法可能會(huì)導(dǎo)致泄漏敏感的應(yīng)用程序數(shù)據(jù):
? |
存儲(chǔ)沒有加密的數(shù)據(jù)
|
? |
弱授權(quán)
|
? |
弱加密
|
對策
要防止泄漏敏感的應(yīng)用程序數(shù)據(jù):
? |
使用強(qiáng)加密機(jī)制來確保數(shù)據(jù)的安全。
|
? |
在執(zhí)行數(shù)據(jù)訪問之前先為每個(gè)調(diào)用方授權(quán),以便用戶只能看到其各自的數(shù)據(jù)。
|
數(shù)據(jù)庫架構(gòu)和連接詳細(xì)信息的泄露
如果您的代碼向客戶端返回異常詳細(xì)信息,惡意用戶可能會(huì)使用這些信息來攻擊服務(wù)器。數(shù)據(jù)訪問代碼中的異常可能會(huì)透露敏感信息,如數(shù)據(jù)庫架構(gòu)詳細(xì)信息、數(shù)據(jù)存儲(chǔ)的特性以及 SQL 代碼片斷。
漏洞
下列漏洞可能會(huì)導(dǎo)致信息泄漏:
? |
不充分的異常處理
|
? |
薄弱的 ASP.NET 配置(允許未經(jīng)處理的異常詳細(xì)信息返回到客戶端)
|
對策
要防止這種泄漏:
? |
捕獲、記錄和處理數(shù)據(jù)訪問代碼中的數(shù)據(jù)訪問異常。
|
? |
向調(diào)用方返回一般的錯(cuò)誤消息。這要求對 Web.config 或 Machine.config 配置文件中的 <customErrors> 元素進(jìn)行適當(dāng)?shù)呐渲谩?/p>
|
未授權(quán)的訪問
在授權(quán)不足的情況下,用戶也許能夠看到另一個(gè)用戶的數(shù)據(jù),并且能夠訪問其他受限制的數(shù)據(jù)。
漏洞
下列做法可能會(huì)允許未授權(quán)的訪問:
? |
數(shù)據(jù)訪問代碼中缺乏授權(quán),從而提供了不受限制的訪問
|
? |
數(shù)據(jù)庫帳戶的特權(quán)過高
|
對策
要防止未授權(quán)的訪問:
? |
按照主體權(quán)限需求為調(diào)用用戶授權(quán)。
|
? |
按照代碼訪問安全權(quán)限需求為調(diào)用代碼授權(quán)。
|
? |
使用受限制的權(quán)限來限制應(yīng)用程序登錄到數(shù)據(jù)庫,并防止直接訪問表格。
|
網(wǎng)絡(luò)竊聽
大多數(shù)應(yīng)用程序的部署體系結(jié)構(gòu)中都包括數(shù)據(jù)訪問代碼與數(shù)據(jù)庫服務(wù)器之間的物理隔離。因此,必須防止竊聽者通過網(wǎng)絡(luò)竊聽敏感數(shù)據(jù)(如應(yīng)用程序特定的數(shù)據(jù)或數(shù)據(jù)庫登錄憑據(jù))。
漏洞
下列做法會(huì)增加網(wǎng)絡(luò)竊聽的漏洞:
? |
在 SQL 身份驗(yàn)證過程中通過網(wǎng)絡(luò)傳遞的明文憑據(jù)
|
? |
進(jìn)出數(shù)據(jù)庫服務(wù)器的未加密敏感應(yīng)用程序數(shù)據(jù)
|
對策
要限制網(wǎng)絡(luò)竊聽的漏洞:
? |
使用 Windows 身份驗(yàn)證來避免通過網(wǎng)絡(luò)發(fā)送憑據(jù)。
|
? |
在數(shù)據(jù)庫服務(wù)器上安裝一個(gè)服務(wù)器證書。這會(huì)導(dǎo)致自動(dòng)加密網(wǎng)絡(luò)上的 SQL 憑據(jù)。
|
? |
在 Web 服務(wù)器和數(shù)據(jù)庫服務(wù)器之間使用 SSL 連接來保護(hù)敏感的應(yīng)用程序數(shù)據(jù)。這需要一個(gè)數(shù)據(jù)庫服務(wù)器證書。
|
? |
在 Web 和數(shù)據(jù)庫服務(wù)器之間使用 IPSec 加密通道。
|
設(shè)計(jì)注意事項(xiàng)
在開始編寫代碼之前,需要在設(shè)計(jì)時(shí)考慮許多重要的問題。主要的注意事項(xiàng)包括:
? |
使用 Windows 身份驗(yàn)證。
|
? |
使用最小特權(quán)帳戶。
|
? |
使用存儲(chǔ)過程。
|
? |
保護(hù)所存儲(chǔ)的敏感數(shù)據(jù)。
|
? |
使用單獨(dú)的數(shù)據(jù)訪問程序集。
|
使用 Windows 身份驗(yàn)證
理想情況下,在設(shè)計(jì)中應(yīng)該使用 Windows 身份驗(yàn)證,以增加安全性好處。使用 Windows 身份驗(yàn)證,您不必存儲(chǔ)具有嵌入憑據(jù)的數(shù)據(jù)庫連接字符串,憑據(jù)不通過網(wǎng)絡(luò)傳遞,而且您可以受益于安全帳戶和密碼管理策略。但是,您需要認(rèn)真考慮在使用 Windows 身份驗(yàn)證時(shí),將使用哪個(gè)帳戶連接到 SQL Server。
有關(guān)詳細(xì)信息,請參閱本模塊后面的身份驗(yàn)證。
使用最小特權(quán)帳戶
您的應(yīng)用程序應(yīng)該使用在數(shù)據(jù)庫中具有有限權(quán)限的最小特權(quán)帳戶。請確保對應(yīng)用程序的數(shù)據(jù)庫登錄進(jìn)行了適當(dāng)?shù)氖跈?quán)和限制。有關(guān)詳細(xì)信息,請參閱本模塊后面的授權(quán)。
使用最小特權(quán)帳戶可以降低風(fēng)險(xiǎn),并在您的帳戶發(fā)生泄漏或者注入了惡意代碼時(shí)限制潛在的損害。對于 SQL 注入,該命令將在由應(yīng)用程序登錄定義的安全上下文中執(zhí)行,并遵守該登錄在數(shù)據(jù)庫中擁有的相關(guān)權(quán)限。如果您使用特權(quán)過高的帳戶(例如,作為 SQL Server sysadmin 角色的成員)進(jìn)行連接,攻擊者能夠在服務(wù)器上的任何數(shù)據(jù)庫中執(zhí)行任意操作。這包括插入、更新和刪除數(shù)據(jù);刪除表;執(zhí)行操作系統(tǒng)命令。
要點(diǎn) 不要使用 sa 帳戶或者 SQL Server sysadmin 或 db_owner 角色的任何成員帳戶連接到 SQL Server。
使用存儲(chǔ)過程
存儲(chǔ)過程提供性能、維護(hù)和安全性好處。應(yīng)盡可能使用參數(shù)化存儲(chǔ)過程進(jìn)行數(shù)據(jù)訪問。安全性好處包括:
? |
可以限制應(yīng)用程序數(shù)據(jù)庫登錄,以便它只具有執(zhí)行指定存儲(chǔ)過程的權(quán)限。沒有必要授予直接的表格訪問權(quán)限。這有助于降低由 SQL 注入攻擊造成的風(fēng)險(xiǎn)。
|
? |
針對傳遞到存儲(chǔ)過程的所有輸入數(shù)據(jù)執(zhí)行長度和類型檢查。同樣,不能將參數(shù)視為可執(zhí)行代碼。這也會(huì)降低 SQL 注入風(fēng)險(xiǎn)。
|
如果由于某種原因,您無法使用參數(shù)化存儲(chǔ)過程,但是您需要?jiǎng)討B(tài)構(gòu)造 SQL 語句,請使用類型化參數(shù)和參數(shù)占位符來構(gòu)造這樣的語句,以確保檢查輸入數(shù)據(jù)的長度和類型。
保護(hù)所存儲(chǔ)的敏感數(shù)據(jù)
標(biāo)識需要保證私密性和完整性的存儲(chǔ)數(shù)據(jù)。如果您只是為了驗(yàn)證而將密碼存儲(chǔ)到數(shù)據(jù)庫中,請考慮使用單向哈希。如果密碼表發(fā)生泄漏,則不能使用哈希來獲取明文密碼。
如果您存儲(chǔ)用戶提供的敏感數(shù)據(jù)(如信用卡號),請使用強(qiáng)對稱加密算法(如三重 DES (3DES))來加密數(shù)據(jù)。使用 Win32 數(shù)據(jù)保護(hù) API (DPAPI) 加密 3DES 密鑰,然后將已加密的密鑰存儲(chǔ)在具有受限 ACL 的注冊表項(xiàng)中,只有管理員和應(yīng)用程序進(jìn)程帳戶才能使用該注冊表項(xiàng)。
為什么不使用 DPAPI?
盡管建議使用 DPAPI 來加密連接字符串和其他可在計(jì)算機(jī)出現(xiàn)故障時(shí)手動(dòng)恢復(fù)和重新構(gòu)造的機(jī)密(如帳戶憑據(jù)),但仍不太適合存儲(chǔ)信用卡號之類的數(shù)據(jù)。這是由于可恢復(fù)性問題(如果密鑰丟失,則無法恢復(fù)加密數(shù)據(jù))和 Web 場問題。相反,應(yīng)該使用對稱加密算法(如 3DES)并使用 DPAPI 加密密鑰。
下面概述了造成 DPAPI 不太適合在數(shù)據(jù)庫中存儲(chǔ)敏感數(shù)據(jù)的主要問題:
? |
如果 DPAPI 與計(jì)算機(jī)密鑰一起使用,而且您將 CRYPTPROTECT_LOCAL_MACHINE 傳遞到 CryptProtectData 和 CryptUnprotectData 函數(shù),則計(jì)算機(jī)帳戶會(huì)生成加密密鑰。這意味著 Web 場中的每臺服務(wù)器都有一個(gè)不同的密鑰,這會(huì)禁止一臺服務(wù)器訪問由另一臺服務(wù)器加密的數(shù)據(jù)。同樣,如果該 Web 服務(wù)器計(jì)算機(jī)被破壞,則密鑰會(huì)丟失,而且加密的數(shù)據(jù)無法從數(shù)據(jù)庫進(jìn)行恢復(fù)。
|
? |
如果使用計(jì)算機(jī)密鑰方法,則該計(jì)算機(jī)上的任何用戶都可以對數(shù)據(jù)進(jìn)行解密(除非您使用其他加密機(jī)制)。
|
? |
如果您將 DPAPI 與用戶密鑰一起使用,而且您使用的是本地用戶帳戶,就會(huì)為每臺 Web 服務(wù)器上的每個(gè)本地帳戶生成一個(gè)不同的安全標(biāo)識符 (SID) 和一個(gè)不同的密鑰,這會(huì)禁止一臺服務(wù)器訪問由另一臺服務(wù)器加密的數(shù)據(jù)。
|
? |
如果您將 DPAPI 與用戶密鑰一起使用,而且您在 Web 場中的計(jì)算機(jī)之間使用漫游用戶配置文件,則所有數(shù)據(jù)都將共享相同的加密/解密密鑰。但是,如果負(fù)責(zé)漫游用戶配置文件帳戶的域控制器被損害或被破壞,則無法重新創(chuàng)建具有相同 SID 的用戶帳戶,而且不能從數(shù)據(jù)庫中恢復(fù)加密的數(shù)據(jù)。
另外,對于漫游用戶配置文件,如果某人設(shè)法檢索該數(shù)據(jù),則只要攻擊者能夠在特定的用戶帳戶下運(yùn)行代碼,就可以在網(wǎng)絡(luò)中的任何計(jì)算機(jī)上解密該數(shù)據(jù)。這會(huì)增加潛在攻擊的范圍,因此不建議這樣做。
|
使用單獨(dú)的數(shù)據(jù)訪問程序集
如果您可以進(jìn)行選擇,請避免將數(shù)據(jù)訪問邏輯直接放在 ASP.NET 頁或代碼隱藏文件中。如果將數(shù)據(jù)訪問邏輯放在一個(gè)單獨(dú)的程序集中,并實(shí)現(xiàn)一個(gè)與應(yīng)用程序的業(yè)務(wù)和表示邏輯分開的邏輯數(shù)據(jù)訪問層,就會(huì)帶來安全性、重復(fù)使用和維護(hù)好處。
從安全的角度看,您可以:
? |
對程序集使用強(qiáng)名稱以提供可防篡改功能。
|
? |
使用沙盒技術(shù)來隔離數(shù)據(jù)訪問代碼,如果您的代碼需要支持部分信任調(diào)用方(例如,部分信任 Web 應(yīng)用程序),這一點(diǎn)十分重要。
|
? |
使用那些按照代碼標(biāo)識權(quán)限需求向調(diào)用代碼授權(quán)的數(shù)據(jù)訪問方法和類。
|
對于深層防御,請按照業(yè)務(wù)組件中的主體權(quán)限需求來執(zhí)行基于主體的授權(quán),并按照代碼標(biāo)識權(quán)限需求來為調(diào)用數(shù)據(jù)訪問邏輯的代碼授權(quán),如圖 14.2 所示。
圖 14.2
表示層、業(yè)務(wù)層和數(shù)據(jù)訪問層的分離
有關(guān)數(shù)據(jù)訪問代碼授權(quán)的詳細(xì)信息,請參閱本模塊后面的授權(quán)部分。
輸入驗(yàn)證
除了業(yè)務(wù)層需要確保數(shù)據(jù)庫保持?jǐn)?shù)據(jù)的有效性和一致性之外,還必須在將數(shù)據(jù)提交到數(shù)據(jù)庫之前驗(yàn)證數(shù)據(jù),以防 SQL 注入。如果數(shù)據(jù)訪問代碼從當(dāng)前信任邊界內(nèi)部的其他組件接收其輸入內(nèi)容,而且您知道數(shù)據(jù)已經(jīng)過驗(yàn)證(例如,由 ASP.NET 網(wǎng)頁或業(yè)務(wù)組件驗(yàn)證),則數(shù)據(jù)訪問代碼會(huì)忽略廣泛的數(shù)據(jù)驗(yàn)證。但是,請確保在數(shù)據(jù)訪問代碼中使用 SQL 參數(shù),這些參數(shù)驗(yàn)證輸入?yún)?shù)的類型和長度。下一部分將討論 SQL 參數(shù)的用法。
SQL 注入
當(dāng)應(yīng)用程序使用輸入內(nèi)容來構(gòu)造動(dòng)態(tài) SQL 語句以訪問數(shù)據(jù)庫時(shí),會(huì)發(fā)生 SQL 注入攻擊。如果代碼使用存儲(chǔ)過程,而這些存儲(chǔ)過程作為包含未篩選的用戶輸入的字符串來傳遞,也會(huì)發(fā)生 SQL 注入攻擊。SQL 注入可能導(dǎo)致攻擊者能夠使用應(yīng)用程序登錄在數(shù)據(jù)庫中執(zhí)行命令。如果應(yīng)用程序使用特權(quán)過高的帳戶連接到數(shù)據(jù)庫,這種問題會(huì)變得很嚴(yán)重。
注意 傳統(tǒng)的安全措施(如使用 SSL 和 IPSec)不能防止 SQL 注入攻擊。
防止 SQL 注入
使用下列對策來防止 SQL 注入攻擊:
? |
限制輸入。
|
? |
使用類型安全的 SQL 參數(shù)。
|
限制輸入
驗(yàn)證輸入內(nèi)容的類型、長度、格式和范圍。如果您不希望獲得數(shù)值,則不要接受它們。應(yīng)該考慮輸入內(nèi)容來自何處。如果它來自受信任源,而且您知道已針對該來源執(zhí)行過徹底的輸入驗(yàn)證,則可以選擇在數(shù)據(jù)訪問代碼中忽略數(shù)據(jù)驗(yàn)證。如果數(shù)據(jù)來自不受信任源或者用于深層防御,則數(shù)據(jù)訪問方法和組件應(yīng)該驗(yàn)證輸入。
使用類型安全的 SQL 參數(shù)
SQL 中的 Parameters 集合提供類型檢查和長度驗(yàn)證。如果您使用 Parameters 集合,則輸入內(nèi)容將被視為文本值,SQL 不會(huì)將其視為可執(zhí)行代碼。使用 Parameters 集合還有一個(gè)好處,那就是可以強(qiáng)制進(jìn)行類型和長度檢查。超出范圍的值會(huì)觸發(fā)異常。這是深層防御的一個(gè)有力示例。
要點(diǎn) SSL 不能防止 SQL 注入。對于任何應(yīng)用程序來說,如果它在沒有正確的輸入驗(yàn)證和適當(dāng)?shù)臄?shù)據(jù)訪問技術(shù)的情況下訪問數(shù)據(jù)庫,都很容易受到 SQL 注入攻擊。
盡可能使用存儲(chǔ)過程,并使用 Parameters 集合來調(diào)用它們。
結(jié)合使用 Parameters 集合和存儲(chǔ)過程
下面的代碼片斷闡釋了 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 參數(shù)被視為文本值,而非可執(zhí)行代碼。另外,還針對參數(shù)進(jìn)行了類型和長度檢查。在上例中,輸入值不能長于 11 個(gè)字符。如果數(shù)據(jù)不遵循由參數(shù)定義的類型或長度,就會(huì)生成異常。
請注意,使用存儲(chǔ)過程不一定會(huì)防止 SQL 注入。重要的是結(jié)合使用參數(shù)和存儲(chǔ)過程。如果不使用參數(shù),則在存儲(chǔ)過程使用未篩選的輸入內(nèi)容時(shí),它們很容易受到 SQL 注入攻擊。例如,下面的代碼片斷很容易受到攻擊:
SqlDataAdapter myCommand = new SqlDataAdapter("LoginStoredProcedure '" +
Login.Text + "'", conn);
要點(diǎn) 如果使用存儲(chǔ)過程,請確保同時(shí)使用參數(shù)。
結(jié)合使用 Parameters 集合和動(dòng)態(tài) SQL
如果您不能使用存儲(chǔ)過程,仍可以使用參數(shù),如下面的代碼片斷所示:
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;
使用參數(shù)批處理
通常會(huì)產(chǎn)生如下誤解:如果將幾個(gè) SQL 語句連接在一起,以便在單個(gè)往返中向服務(wù)器發(fā)送一批語句,則不能使用參數(shù)。但是,如果您能確保參數(shù)名不重復(fù),則可以使用這種技術(shù)。通過在 SQL 文本連接過程中,向每個(gè)參數(shù)名中添加一個(gè)數(shù)字或其他某個(gè)唯一值,可以方便地執(zhí)行此操作。
使用篩選例程
用來防止 SQL 注入攻擊的另一種方法是開發(fā)篩選例程,以便向具有特殊 SQL 含義的字符添加轉(zhuǎn)義符,如單撇號字符。下面的代碼片斷闡釋了一個(gè)用來添加轉(zhuǎn)義符的篩選例程:
private string SafeSqlLiteral(string inputSQL)
{
return inputSQL.Replace("'", "''");
}
這種例程存在著一定的問題,而且您不應(yīng)完全依賴它們,因?yàn)楣粽呖梢允褂?ASCII 十六進(jìn)制字符來回避檢查。但是應(yīng)該篩選輸入內(nèi)容,并將其作為深層防御策略的一部分。
注意 不要依賴篩選輸入。
使用 LIKE 子句
請注意,如果您使用 LIKE 子句,通配符仍需要轉(zhuǎn)義符。下面的代碼片斷闡釋了這種技術(shù):
s = s.Replace("[", "[[]");
s = s.Replace("%", "[%]");
s = s.Replace("_", "[_]");
身份驗(yàn)證
當(dāng)應(yīng)用程序連接到 SQL Server 數(shù)據(jù)庫時(shí),可以在 Windows 身份驗(yàn)證或 SQL 身份驗(yàn)證之間進(jìn)行選擇。Windows 身份驗(yàn)證更安全。如果必須使用 SQL 身份驗(yàn)證(可能由于必須使用許多不同的帳戶連接到數(shù)據(jù)庫,并且希望避免調(diào)用 LogonUser),請執(zhí)行其他幾個(gè)步驟,以盡可能降低額外的風(fēng)險(xiǎn)。
注意 如果使用 LogonUser 創(chuàng)建模擬令牌,需要在 Microsoft Windows 2000 上具有功能強(qiáng)大的“作為操作系統(tǒng)的一部分工作”特權(quán),因此應(yīng)避免使用此方法。
考慮下面的建議:
? |
使用 Windows 身份驗(yàn)證。
|
? |
保護(hù) SQL 身份驗(yàn)證的憑據(jù)。
|
? |
使用最小特權(quán)帳戶進(jìn)行連接。
|
使用 Windows 身份驗(yàn)證
Windows 身份驗(yàn)證不通過網(wǎng)絡(luò)發(fā)送憑據(jù)。如果對 Web 應(yīng)用程序使用 Windows 身份驗(yàn)證,請盡可能使用服務(wù)帳戶或進(jìn)程帳戶(如 ASPNET 帳戶)連接到數(shù)據(jù)庫。Windows 和 SQL Server 都必須能夠識別您在數(shù)據(jù)庫服務(wù)器上使用的帳戶。該帳戶必須被授予 SQL Server 登錄權(quán)限,而且需要具有與訪問數(shù)據(jù)庫相關(guān)的登錄權(quán)限。
在使用 Windows 身份驗(yàn)證時(shí),必須使用受信任連接。下面的代碼片斷顯示了幾個(gè)使用 Windows 身份驗(yàn)證的典型的連接字符串。
下例使用面向 SQL Server 的 ADO.NET 數(shù)據(jù)提供程序:
SqlConnection pubsConn = new SqlConnection(
"server=dbserver; database=pubs; Integrated Security=SSPI;");
下例使用面向 OLE DB 數(shù)據(jù)源的 ADO.NET 數(shù)據(jù)提供程序:
OleDbConnection pubsConn = new OleDbConnection(
"Provider=SQLOLEDB; Data Source=dbserver; Integrated Security=SSPI;" +
"Initial Catalog=northwind");
保護(hù) SQL 身份驗(yàn)證的憑據(jù)
如果必須使用 SQL 身份驗(yàn)證,請確保憑據(jù)不以明文形式通過網(wǎng)絡(luò)發(fā)送,并確保加密包含憑據(jù)的數(shù)據(jù)庫連接字符串。
要使 SQL Server 能夠自動(dòng)加密通過網(wǎng)絡(luò)發(fā)送的憑據(jù),請?jiān)跀?shù)據(jù)庫服務(wù)器上安裝服務(wù)器證書。也可以在 Web 服務(wù)器和數(shù)據(jù)庫服務(wù)器之間使用 IPSec 加密通道,來確保進(jìn)出數(shù)據(jù)庫服務(wù)器的所有通信的安全。要確保連接字符串的安全,請使用 DPAPI。有關(guān)詳細(xì)信息,請參閱本模塊后面配置管理部分中的“確保連接字符串的安全”。
使用最小特權(quán)帳戶進(jìn)行連接
應(yīng)用程序應(yīng)該通過使用最小特權(quán)帳戶連接到數(shù)據(jù)庫。如果您使用 Windows 身份驗(yàn)證進(jìn)行連接,則從操作系統(tǒng)的角度看,Windows 帳戶應(yīng)該是最小特權(quán)帳戶,而且該帳戶應(yīng)該具有有限的特權(quán)和對 Windows 資源的有限的訪問能力。另外,無論您使用 Windows 身份驗(yàn)證還是 SQL 身份驗(yàn)證,相應(yīng)的 SQL Server 登錄都應(yīng)該受到數(shù)據(jù)庫中的權(quán)限的限制。
有關(guān)如何創(chuàng)建最小特權(quán)數(shù)據(jù)庫帳戶以及使用 Windows 身份驗(yàn)證將 ASP.NET Web 應(yīng)用程序連接到遠(yuǎn)程數(shù)據(jù)庫的選項(xiàng)的詳細(xì)信息,請參閱模塊 19 確保 ASP.NET 應(yīng)用程序和 Web 服務(wù)的安全中的“數(shù)據(jù)訪問”。
授權(quán)
如果用戶能夠檢索和操縱特定的數(shù)據(jù),就會(huì)建立授權(quán)過程。有兩種方法:數(shù)據(jù)訪問代碼可以使用授權(quán)來確定是否執(zhí)行請求的操作,數(shù)據(jù)庫可以通過執(zhí)行授權(quán)來限制應(yīng)用程序所使用的 SQL 登錄的功能。
在授權(quán)不足的情況下,用戶也許能夠看到另一個(gè)用戶的數(shù)據(jù),未授權(quán)的用戶也許能夠訪問受限制的數(shù)據(jù)。要去除這些威脅:
? |
限制未授權(quán)的調(diào)用方。
|
? |
限制未授權(quán)的代碼。
|
? |
限制數(shù)據(jù)庫中的應(yīng)用程序。
|
圖 14.3 概述了應(yīng)該使用的授權(quán)點(diǎn)和技術(shù)。
圖 14.3
數(shù)據(jù)訪問授權(quán)、程序集和數(shù)據(jù)庫
注意數(shù)據(jù)訪問代碼如何按照權(quán)限需求為調(diào)用用戶或調(diào)用代碼授權(quán)。代碼標(biāo)識需求是 .NET 代碼訪問安全性的一個(gè)特性。
要為數(shù)據(jù)庫中的應(yīng)用程序授權(quán),請使用只具有執(zhí)行選定存儲(chǔ)過程權(quán)限的最小特權(quán) SQL 服務(wù)器登錄。除非有特殊理由,否則不應(yīng)為應(yīng)用程序授予如下權(quán)限:直接針對任何表執(zhí)行創(chuàng)建、檢索、更新、破壞/刪除 (CRUD) 操作。
注意 存儲(chǔ)過程在數(shù)據(jù)庫系統(tǒng)的安全上下文中運(yùn)行。盡管您可以通過為應(yīng)用程序分配對特定存儲(chǔ)過程的權(quán)限來限制它的邏輯操作,但不能限制存儲(chǔ)過程所執(zhí)行的操作的結(jié)果。存儲(chǔ)過程是受信任代碼。必須使用數(shù)據(jù)庫權(quán)限來確保存儲(chǔ)過程的接口安全。
限制未授權(quán)的調(diào)用方
您的代碼在連接到數(shù)據(jù)庫之前必須基于角色或標(biāo)識為用戶授權(quán)。角色檢查通常用在應(yīng)用程序的業(yè)務(wù)邏輯中,但是,如果您不能清楚地區(qū)分業(yè)務(wù)邏輯和數(shù)據(jù)訪問邏輯,請對數(shù)據(jù)庫訪問方法使用主體權(quán)限需求。
以下屬性確保只有作為 Manager 角色成員的用戶才能調(diào)用 DisplayCustomerInfo 方法:
[PrincipalPermissionAttribute(SecurityAction.Demand, Role="Manager")]
public void DisplayCustomerInfo(int CustId)
{
}
如果需要其他授權(quán)粒度,并且需要在數(shù)據(jù)訪問方法內(nèi)部執(zhí)行基于角色的邏輯,請使用命令性主體權(quán)限需求或顯式的角色檢查,如下面的代碼片斷所示:
using System.Security;
using System.Security.Permissions;
public void DisplayCustomerInfo(int CustId)
{
try
{
// 用來驗(yàn)證調(diào)用方是 manager 的命令性主體權(quán)限
// 角色檢查
PrincipalPermission principalPerm = new PrincipalPermission(
null, "Manager");
// 僅在調(diào)用方是“Manager”角色的成員時(shí)才執(zhí)行
// 隨后的代碼
}
catch( SecurityException ex )
{
. . .
}
}
下面的代碼片斷使用顯式的程序設(shè)計(jì)角色檢查來確保調(diào)用方是 Manager 角色的成員:
public void DisplayCustomerInfo(int CustId)
{
if(!Thread.CurrentPrincipal.IsInRole("Manager"))
{
. . .
}
}
限制未授權(quán)的代碼
通過使用 .NET Framework 代碼訪問安全性(特別是代碼標(biāo)識需求),可以限制能夠訪問數(shù)據(jù)訪問類和方法的程序集。
例如,如果您只希望由公司或特定開發(fā)組織編寫的代碼能夠使用您的數(shù)據(jù)訪問組件,請使用 StrongNameIdentityPermission ,并要求調(diào)用程序集具有一個(gè)帶有指定公鑰的強(qiáng)名稱,如下面的代碼片斷所示:
using System.Security.Permissions;
. . .
[StrongNameIdentityPermission(SecurityAction.LinkDemand,
PublicKey="002...4c6")]
public void GetCustomerInfo(int CustId)
{
}
要提取給定程序集的公鑰的文本表示形式,請使用下面的命令:
sn -Tp assembly.dll
注意 在 –Tp 開關(guān)中使用大寫的“T”。
因?yàn)?Web 應(yīng)用程序的程序集是動(dòng)態(tài)編譯的,所以對于這些程序集不能使用強(qiáng)名稱。這使得很難將數(shù)據(jù)訪問程序集的使用限制在特定的 Web 應(yīng)用程序上。最佳方法是開發(fā)一個(gè)自定義權(quán)限,并要求該權(quán)限來自數(shù)據(jù)訪問組件。完全信任 Web 應(yīng)用程序(或任何完全受信任代碼)可以調(diào)用您的組件。但是,部分信任代碼只有在被授予了自定義權(quán)限之后,才能調(diào)用您的數(shù)據(jù)訪問組件。
有關(guān)自定義權(quán)限的示例實(shí)現(xiàn),請參閱本指南“如何”部分中的如何:創(chuàng)建自定義加密權(quán)限。
限制數(shù)據(jù)庫中的應(yīng)用程序
首選方法是為應(yīng)用程序用來連接到數(shù)據(jù)庫的 Windows 帳戶創(chuàng)建一個(gè) SQL Server 登錄權(quán)限,然后將 SQL Server 登錄映射到數(shù)據(jù)庫中的某個(gè)數(shù)據(jù)庫用戶。將該數(shù)據(jù)庫用戶放在用戶定義的數(shù)據(jù)庫角色中,并授予該角色相應(yīng)的權(quán)限。理想情況下,應(yīng)該只為該角色授予對應(yīng)用程序所使用的存儲(chǔ)過程的執(zhí)行訪問權(quán)限。
有關(guān)如何配置此方法的詳細(xì)信息,請參閱模塊 19 確保 ASP.NET 應(yīng)用程序和 Web 服務(wù)的安全中的“為 ASP.NET 應(yīng)用程序配置數(shù)據(jù)訪問權(quán)限”。
配置管理
數(shù)據(jù)庫連接字符串是針對數(shù)據(jù)訪問代碼主要考慮的配置管理問題。應(yīng)認(rèn)真考慮這些字符串的存儲(chǔ)位置以及如何保護(hù)它們(特別是當(dāng)它們包括憑據(jù)時(shí))。要提高加密管理安全性:
? |
使用 Windows 身份驗(yàn)證。
|
? |
確保連接字符串的安全。
|
? |
使用受限制的 ACL 確保 UDL 文件的安全。
|
使用 Window 身份驗(yàn)證
使用 Windows 身份驗(yàn)證時(shí),系統(tǒng)會(huì)為您管理憑據(jù),而且憑據(jù)不會(huì)通過網(wǎng)絡(luò)傳輸。還可以避免將用戶名和密碼嵌入到連接字符串中。
確保連接字符串的安全
如果您需要使用 SQL 身份驗(yàn)證,連接字符串中將包含用戶名和密碼。如果攻擊者利用 Web 服務(wù)器上的源代碼泄漏這一漏洞或設(shè)法登錄到該服務(wù)器,則攻擊者可以檢索連接字符串。同樣,能夠合法登錄到該服務(wù)器的任何用戶都可以查看它們。使用加密機(jī)制確保連接字符串的安全。
加密連接字符串
使用 DPAPI 加密連接字符串。使用 DPAPI 加密時(shí),由于加密密鑰由平臺進(jìn)行管理,并且綁定到特定的計(jì)算機(jī)或 Windows 用戶帳戶,因此可避免出現(xiàn)加密密鑰管理問題。要使用 DPAPI,必須通過 P/Invoke 調(diào)用 Win32 DPAPI 函數(shù)。
有關(guān)如何構(gòu)建托管包裝類的詳細(xì)信息,請參閱“Microsoft patterns & practices Volume I, Building Secure ASP.NET Web Applications: Authentication, Authorization, and Secure Communication”的“How To”部分中的“How To: Create a DPAPI Library”,其網(wǎng)址為:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/secnetlpMSDN.asp(英文)。
安全地存儲(chǔ)加密的連接字符串
加密的連接字符串可以放在注冊表中,也可以放在 Web.config 或 Machine.config 文件中。如果您使用 HKEY_LOCAL_MACHINE 下的注冊表項(xiàng),請將下面的 ACL 應(yīng)用于該項(xiàng):
管理員:完全控制
進(jìn)程帳戶:讀取
注意 該進(jìn)程帳戶由運(yùn)行數(shù)據(jù)訪問程序集的進(jìn)程來確定。這通常是 ASP.NET 進(jìn)程,或者當(dāng)您的解決方案使用企業(yè)服務(wù)中間層時(shí),該進(jìn)程是企業(yè)服務(wù)服務(wù)器進(jìn)程。
還可以考慮使用 HKEY_CURRENT_USER,該注冊表項(xiàng)提供受限制的訪問。有關(guān)詳細(xì)信息,請參閱模塊 7 構(gòu)建安全的程序集中的“注冊表”部分。
注意 如果您使用 Microsoft Visual Studio? .NET 數(shù)據(jù)庫連接向?qū)ВB接字符串將以明文屬性值形式存儲(chǔ)在 Web 應(yīng)用程序代碼隱藏文件或 Web.config 文件中。這兩種方法都應(yīng)該避免使用。
為了更容易部署,您可能希望將加密的字符串存儲(chǔ)在 Web.config 中,盡管這可能不如使用受限制的注冊表項(xiàng)安全。在本例中,將使用如下所示的自定義 <appSettings> 名稱-值對:
<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 設(shè)置為“True”或“Yes”
如果在連接字符串中包括 Persist Security Info 屬性,將導(dǎo)致 ConnectionString 屬性在密碼返回給用戶之前,將密碼從連接字符串中去除。在建立與數(shù)據(jù)庫的連接之后,默認(rèn)設(shè)置 false(等同于忽略 Persist Security Info 屬性)會(huì)丟棄該信息。
使用受限制的 ACL 確保 UDL 文件的安全
如果您的應(yīng)用程序結(jié)合使用外部通用數(shù)據(jù)鏈接 (UDL) 文件和面向 OLE DB 的 ADO.NET 托管數(shù)據(jù)提供程序,請使用 NTFS 權(quán)限來限制訪問。使用以下受限制的 ACL:
管理員:完全控制
進(jìn)程帳戶:讀取
注意 UDL 文件未進(jìn)行加密。一個(gè)更安全的方法是,使用 DPAPI 加密連接字符串,并將其存儲(chǔ)在受限制的注冊表項(xiàng)中。
敏感數(shù)據(jù)
許多 Web 應(yīng)用程序都在數(shù)據(jù)庫中以某種形式存儲(chǔ)敏感數(shù)據(jù)。如果攻擊者設(shè)法針對您的數(shù)據(jù)庫執(zhí)行查詢,則務(wù)必要適當(dāng)?shù)丶用芩忻舾袛?shù)據(jù)項(xiàng)(如信用卡號)。
? |
加密需要存儲(chǔ)的敏感數(shù)據(jù)。
|
? |
確保網(wǎng)絡(luò)上敏感數(shù)據(jù)的安全。
|
? |
使用帶有salt 的哈希值存儲(chǔ)密碼。
|
加密需要存儲(chǔ)的敏感數(shù)據(jù)
盡可能避免存儲(chǔ)敏感數(shù)據(jù)。如果必須存儲(chǔ)敏感數(shù)據(jù),請對其進(jìn)行加密。
使用 3DES 加密
要將敏感數(shù)據(jù)(如信用卡號)存儲(chǔ)在數(shù)據(jù)庫中,請使用強(qiáng)對稱加密算法,如 3DES。
? |
在開發(fā)過程中,要啟用 3DES 加密
1.
|
使用 RNGCryptoServiceProvider 類來生成強(qiáng)(192 位,24 字節(jié))加密密鑰。
|
2.
|
備份加密密鑰,并將備份副本存儲(chǔ)在物理安全的位置。
|
3.
|
使用 DPAPI 對密鑰進(jìn)行加密,并將其存儲(chǔ)在注冊表項(xiàng)中。使用下面的 ACL 來確保注冊表項(xiàng)的安全:
管理員:完全控制
進(jìn)程帳戶(例如,ASPNET):讀取
|
|
? |
在運(yùn)行時(shí),要將加密數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫中
1.
|
獲取要加密的數(shù)據(jù)。
|
2.
|
從注冊表中檢索經(jīng)過加密的加密密鑰。
|
3.
|
使用 DPAPI 對加密密鑰進(jìn)行解密。
|
4.
|
結(jié)合使用 TripleDESCryptoServiceProvider 類和加密密鑰來加密數(shù)據(jù)。
|
5.
|
將加密數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫中。
|
|
? |
在運(yùn)行時(shí),要對加密的機(jī)密進(jìn)行解密
1.
|
從數(shù)據(jù)庫中檢索加密數(shù)據(jù)。
|
2.
|
從注冊表中檢索經(jīng)過加密的加密密鑰。
|
3.
|
使用 DPAPI 對加密密鑰進(jìn)行解密。
|
4.
|
使用 TripleDESCryptoServiceProvider 類對加密數(shù)據(jù)進(jìn)行解密。
|
|
在此過程中,如果用來對加密密鑰進(jìn)行加密的 DPAPI 帳戶被損壞,則可以從備份位置檢索 3DES 密鑰的備份,并在新帳戶下使用 DPAPI 對該備份進(jìn)行加密。新的加密密鑰可以存儲(chǔ)在注冊表中,數(shù)據(jù)庫中的數(shù)據(jù)仍可以進(jìn)行解密。
有關(guān)創(chuàng)建托管 DPAPI 庫的詳細(xì)信息,請參閱“Microsoft patterns & practices Volume I, Building Secure ASP.NET Web Applications: Authentication, Authorization, and Secure Communication”中“How To”部分中的“How To: Create a DPAPI Library”,其網(wǎng)址為:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/secnetlpMSDN.asp(英文)。
保護(hù)網(wǎng)絡(luò)上敏感數(shù)據(jù)的安全
通過網(wǎng)絡(luò)傳入和傳出數(shù)據(jù)庫服務(wù)器的敏感數(shù)據(jù)可能包括應(yīng)用程序特定的數(shù)據(jù)或數(shù)據(jù)庫登錄憑據(jù)。為了確保網(wǎng)絡(luò)上的數(shù)據(jù)的私密性和完整性,可以使用平臺級解決方案(如由安全數(shù)據(jù)中心提供的解決方案,在這種解決方案中,在服務(wù)器之間使用 IPSec 加密的通信通道),也可以對您的應(yīng)用程序進(jìn)行配置,以便與數(shù)據(jù)庫建立 SSL 連接。后一種方法需要在數(shù)據(jù)庫服務(wù)器上安裝服務(wù)器證書。
有關(guān)使用 SSL 和 IPSec 的詳細(xì)信息,請參閱“Microsoft patterns & practices Volume I, Building Secure ASP.NET Web Applications: Authentication, Authorization, and Secure Communication”的“How To”部分中的“How To: Use IPSec to Provide Secure Communication Between Two Servers”和“How To:Use SSL to Secure Communication to SQL Server 2000”,其網(wǎng)址為:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/secnetlpMSDN.asp(英文)
使用帶有salt 的哈希值存儲(chǔ)密碼
如果您需要實(shí)現(xiàn)一個(gè)包含用戶名和密碼的用戶存儲(chǔ),請不要以明文或加密格式來存儲(chǔ)密碼。存儲(chǔ)增加了 salt 的不可逆哈希值(而不是存儲(chǔ)密碼)可以降低詞典攻擊的風(fēng)險(xiǎn)。
注意 salt 值是密碼形式的強(qiáng)隨機(jī)數(shù)字。
創(chuàng)建 Salt 值
下面的代碼顯示了如何通過使用隨機(jī)數(shù)字生成功能(此功能由 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);
}
創(chuàng)建 Hash 值(帶有 Salt)
下面的代碼片斷顯示了如何從所提供的密碼和 salt 值生成哈希值。
public static string CreatePasswordHash(string pwd, string salt)
{
string saltAndPwd = string.Concat(pwd, salt);
string hashedPwd =
FormsAuthentication.HashPasswordForStoringInConfigFile(
saltAndPwd, "SHA1");
return hashedPwd;
}
詳細(xì)信息
有關(guān)實(shí)現(xiàn)用戶存儲(chǔ)(使用帶有salt 的哈希值存儲(chǔ)密碼)的詳細(xì)信息,請參閱“Microsoft patterns & practices Volume I, Building Secure ASP.NET Web Applications: Authentication, Authorization, and Secure Communication”的“How To”部分中的“How To: Use Forms Authentication with SQL Server 2000”,其網(wǎng)址為:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/secnetlpMSDN.asp(英文)。
異常管理
異常條件可能會(huì)由配置錯(cuò)誤、代碼中的錯(cuò)誤或惡意輸入引起。如果沒有正確的異常管理,這些條件可能會(huì)透露有關(guān)數(shù)據(jù)源位置和特性的敏感信息,以及有價(jià)值的連接詳細(xì)信息。下面的建議適用于數(shù)據(jù)訪問代碼:
? |
捕獲和記錄 ADO.NET 異常。
|
? |
確保數(shù)據(jù)庫連接總是處于斷開狀態(tài)。
|
? |
在 ASP.NET 應(yīng)用程序中使用一般錯(cuò)誤頁面。
|
捕獲和記錄 ADO.NET 異常
將數(shù)據(jù)訪問代碼放在 try/catch 塊中并處理異常。在編寫 ADO.NET 數(shù)據(jù)訪問代碼時(shí),由 ADO.NET 生成的異常類型取決于數(shù)據(jù)提供程序。例如:
? |
SQL Server .NET Framework 數(shù)據(jù)提供程序生成 SqlException,
|
? |
OLE DB .NET Framework 數(shù)據(jù)提供程序生成 OleDbException,
|
? |
ODBC .NET Framework 數(shù)據(jù)提供程序生成 OdbcException。
|
捕獲異常
下面的代碼使用 SQL Server .NET Framework 數(shù)據(jù)提供程序,并顯示應(yīng)該如何捕獲類型為 SqlException 的異常。
try
{
// 數(shù)據(jù)訪問代碼
}
catch (SqlException sqlex) // 比較具體
{
}
catch (Exception ex) // 比較一般
{
}
記錄異常
還應(yīng)該記錄來自 SqlException 類的詳細(xì)信息。此類公開那些包含異常條件詳細(xì)信息的屬性。這些屬性包括 Message 屬性(用來描述錯(cuò)誤)、Number 屬性(用來唯一標(biāo)識錯(cuò)誤類型)以及 State 屬性(其中包含其他信息)。State 屬性通常用來指示特定錯(cuò)誤條件出現(xiàn)的具體位置。例如,如果某個(gè)存儲(chǔ)過程從多個(gè)行中生成同一錯(cuò)誤,則 State 屬性可以指出錯(cuò)誤出現(xiàn)的具體位置。最后,Errors 集合中包含 SqlError 對象,這些對象提供詳細(xì)的 SQL 服務(wù)器錯(cuò)誤信息。
下面的代碼片斷顯示了如何通過使用 SQL Server .NET Framework 數(shù)據(jù)提供程序來處理 SQL Server 錯(cuò)誤條件:
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
// 由數(shù)據(jù)訪問層 (DAL) 組件公開的方法
public string GetProductName( int ProductID )
{
SqlConnection conn = new SqlConnection(
"server=(local);Integrated Security=SSPI;database=products");
// 將所有的數(shù)據(jù)訪問代碼包含在 try 塊中
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();
// 在該方法返回之前先執(zhí)行 finally 代碼
return paramPN.Value.ToString();
}
catch (SqlException sqlex)
{
// 處理數(shù)據(jù)訪問異常條件
// 記錄具體的異常詳細(xì)信息
LogException(sqlex);
// 將當(dāng)前異常包裝在一個(gè)相關(guān)性更強(qiáng)的
// 外部異常中,并重新引發(fā)新異常
throw new Exception(
"Failed to retrieve product details for product ID: " +
ProductID.ToString(), sqlex );
}
finally
{
conn.Close(); // 確保連接處于斷開狀態(tài)
}
}
// Helper 例程,該例程將 SqlException 詳細(xì)信息記錄到
// 應(yīng)用程序事件日志中
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 );
}
}
確保數(shù)據(jù)庫連接總是處于斷開狀態(tài)
如果發(fā)生異常,一定要斷開數(shù)據(jù)庫連接,并釋放其他所有受限制的資源。使用 finally 塊或 C# using 語句,可以確保無論是否發(fā)生了異常條件,都會(huì)斷開數(shù)據(jù)庫連接。上面的代碼闡釋了 finally 塊的用法。還可以按如下方式使用 C# using 語句:
using ((SqlConnection conn = new SqlConnection(connString)))
{
conn.Open();
// 在以下情況下將斷開連接:生成異常或者控制流
// 通常會(huì)離開 using 語句的使用范圍
}
在 ASP.NET 應(yīng)用程序中使用一般錯(cuò)誤頁面
如果您的數(shù)據(jù)訪問代碼由 ASP.NET Web 應(yīng)用程序或 Web 服務(wù)調(diào)用,則應(yīng)該對 <customErrors> 元素進(jìn)行配置,以防異常詳細(xì)信息傳播回到最終用戶。還可以通過使用該元素來指定一般錯(cuò)誤頁面,如下所示。
<customErrors mode="On" defaultRedirect="YourErrorPage.htm" />
對于生產(chǎn)服務(wù)器設(shè)置 mode="On"。只有在發(fā)布之前開發(fā)和測試軟件時(shí)才使用 mode="Off"。如果不這樣做,將導(dǎo)致向最終用戶返回大量錯(cuò)誤信息(如圖 14.4 中顯示的信息)。這些信息可能包含數(shù)據(jù)庫服務(wù)器的名稱、數(shù)據(jù)庫名稱和連接憑據(jù)。
圖 14.4
詳細(xì)的異常信息會(huì)透露敏感數(shù)據(jù)
圖 14.4 還顯示了數(shù)據(jù)訪問代碼中接近導(dǎo)致異常的行的大量漏洞。特別是:
? |
連接字符串是硬編碼的。
|
? |
特權(quán)極高的 sa 帳戶用于連接到數(shù)據(jù)庫。
|
? |
sa 帳戶有一個(gè)弱密碼。
|
? |
SQL 命令的構(gòu)造容易受到 SQL 注入攻擊;輸入內(nèi)容未進(jìn)行驗(yàn)證,代碼不使用參數(shù)化存儲(chǔ)過程。
|
構(gòu)建安全的數(shù)據(jù)訪問組件
下面的代碼顯示了 CheckProductStockLevel 方法(用來在產(chǎn)品數(shù)據(jù)庫中查詢庫存量)的示例實(shí)現(xiàn),該代碼闡釋了本模塊前面介紹的數(shù)據(jù)訪問代碼的許多重要安全功能。
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) 由 try/catch 塊保護(hù)的代碼
try
{
// (2) 使用正則表達(dá)式驗(yàn)證的輸入內(nèi)容
// 應(yīng)該從資源程序集中檢索錯(cuò)誤消息,以幫助實(shí)現(xiàn)
// 本地化。為簡短起見,省略了 Localization(本地化)代碼。
if (Regex.IsMatch(productCode, "^[A-Za-z0-9]{12}$") == false)
throw new ArgumentException("Invalid product code" );
//(3) using 語句確保連接被斷開
using (SqlConnection conn = new SqlConnection(GetConnectionString()))
{
// (4) 使用參數(shù)化存儲(chǔ)過程可以應(yīng)對
// SQL 注入攻擊
SqlCommand cmd = new SqlCommand("spCheckProduct", conn);
cmd.CommandType = CommandType.StoredProcedure;
// 對參數(shù)的類型進(jìn)行檢查
SqlParameter parm =
cmd.Parameters.Add("@ProductCode",
SqlDbType.VarChar,12);
parm.Value = productCode;
// 定義輸出參數(shù)
SqlParameter retparm = cmd.Parameters.Add("@quantity", SqlDbType.Int);
retparm.Direction = ParameterDirection.Output;
conn.Open();
cmd.ExecuteNonQuery();
quantity = (int)retparm.Value;
}
}
catch (SqlException sqlex)
{
// (5) 記錄完整的異常詳細(xì)信息。一般(安全的)錯(cuò)誤消息
// 基于 SQL 錯(cuò)誤代碼返回調(diào)用方
// 為清楚起見,省略了日志和錯(cuò)誤標(biāo)識代碼
throw new Exception("Error Processing Request");
}
catch (Exception ex)
{
// 記錄完整的異常詳細(xì)信息
throw new Exception("Error Processing Request");
}
return quantity;
}
// (6) 將加密的數(shù)據(jù)庫連接字符串存保留在注冊表中
private static string GetConnectionString()
{
// 從注冊表中檢索密碼文本;進(jìn)程帳戶必須
// 由注冊表項(xiàng)的 ACL 授予“讀取”訪問權(quán)限
string encryptedString = (string)Registry.LocalMachine.OpenSubKey(
@"Software\OrderProcessing\")
.GetValue("ConnectionString");
// 使用托管的 DPAPI helper 庫對該字符串進(jìn)行解密
DataProtector dp = new DataProtector(DataProtector.Store.USE_MACHINE_STORE);
byte[] dataToDecrypt = Convert.FromBase64String(encryptedString);
return Encoding.ASCII.GetString(dp.Decrypt(dataToDecrypt,null));
}
上面的代碼顯示出下列安全特征(由注釋行中的數(shù)字進(jìn)行標(biāo)識)。
1.
|
數(shù)據(jù)訪問代碼放在 try/catch 塊中。這是防止在出現(xiàn)異常時(shí)將系統(tǒng)級信息返回到調(diào)用方所必需的。調(diào)用 ASP.NET Web 應(yīng)用程序或 Web 服務(wù)會(huì)處理異常,并向客戶端返回適當(dāng)?shù)囊话沐e(cuò)誤消息,但是數(shù)據(jù)訪問代碼不依賴這些消息。
|
2.
|
使用正則表達(dá)式驗(yàn)證輸入。檢查所提供的產(chǎn)品 ID,以便驗(yàn)證它只包含 A–Z 和 0–9 范圍內(nèi)的字符,而且不超過 12 個(gè)字符。這是旨在防止 SQL 注入攻擊的一組對策中的第一個(gè)。
|
3.
|
SqlConnection 對象是在 Microsoft Visual C#? using 語句的內(nèi)部創(chuàng)建的。這可確保無論是否發(fā)生了異常,都斷開方法內(nèi)部的連接。這會(huì)降低拒絕服務(wù)攻擊的威脅,該威脅嘗試使用到數(shù)據(jù)庫的所有可用連接。通過使用 finally 塊可以實(shí)現(xiàn)類似的功能。
|
4.
|
參數(shù)化存儲(chǔ)過程用于數(shù)據(jù)訪問。這是防止 SQL 注入的另一個(gè)對策。
|
5.
|
不向客戶端返回詳細(xì)的錯(cuò)誤信息。記錄異常詳細(xì)信息,以便幫助診斷問題。
|
6.
|
加密的數(shù)據(jù)庫連接字符串存儲(chǔ)在注冊表中。存儲(chǔ)數(shù)據(jù)庫連接字符串最安全的方法之一是,使用 DPAPI 加密該字符串,并將加密的密碼文本存儲(chǔ)在具有受限 ACL 的受保護(hù)的注冊表項(xiàng)中。(例如,使用“管理員:完全控制”和“ASP.NET 或企業(yè)服務(wù)進(jìn)程帳戶:讀取”,具體情況取決于由哪個(gè)進(jìn)程托管該組件。)
注意 該代碼顯示了如何從注冊表檢索連接字符串,然后使用托管的 DPAPI helper 庫對其進(jìn)行解密。此庫在“Microsoft patterns & practices Volume I, Building Secure ASP.NET Web Applications: Authentication, Authorization, and Secure Communication”的“How To”部分中的 How To: Create a DPAPI Library (英文)中提供。
|
代碼訪問安全性注意事項(xiàng)
所有的數(shù)據(jù)訪問都遵循代碼訪問安全權(quán)限需求。您所選擇的 ADO.NET 托管數(shù)據(jù)提供程序可以確定精確的要求。下表顯示了對于每個(gè) ADO.NET 數(shù)據(jù)提供程序必須授予數(shù)據(jù)訪問程序集的權(quán)限。
表 14.1:ADO.NET 數(shù)據(jù)提供程序所需的代碼訪問安全權(quán)限
SQL Server
|
SqlClient PermissionSupports 部分信任調(diào)用方(包括中等信任 Web 應(yīng)用程序)。
|
OLE DB
|
OleDbPermission*
|
Oracle
|
OraclePermission*
|
ODBC
|
OdbcPermission*
|
*撰寫本文時(shí),在 1.0 和 1.1 版本的 .NET Framework上,OLE DB、Oracle 和 ODBC 提供程序只支持完全信任調(diào)用方。要從部分信任 Web 應(yīng)用程序中使用這些提供程序,必須將您的數(shù)據(jù)訪問代碼放在沙盒中,這需要一個(gè)專門的數(shù)據(jù)訪問程序集。有關(guān)顯示如何將數(shù)據(jù)訪問代碼放在沙盒中以及如何從中等信任 Web 應(yīng)用程序中使用 OLE DB 數(shù)據(jù)提供程序的示例,請參閱模塊 9 ASP.NET 代碼訪問安全性。
如果您使用 ADO.NET SQL Server 數(shù)據(jù)提供程序,您的代碼就必須由代碼訪問安全策略授予 SqlClientPermission。完全和中等信任 Web 應(yīng)用程序具有此權(quán)限。
代碼能否連接到 SQL Server 由代碼是否被授予了 SqlClientPermission 來確定。還可以使用權(quán)限對數(shù)據(jù)庫連接字符串的使用進(jìn)行限制。例如,可以強(qiáng)制應(yīng)用程序使用集成安全性,或者可以確保在使用 SQL Server 安全性時(shí)不接受空白密碼。如果違反通過 SqlClientPermission 指定的規(guī)則,將會(huì)導(dǎo)致運(yùn)行時(shí)安全異常。
有關(guān)如何使用 SqlClientPermission 來限制數(shù)據(jù)訪問的詳細(xì)信息,請參閱模塊 8 代碼訪問安全的實(shí)踐中的“數(shù)據(jù)訪問”。
部署注意事項(xiàng)
以安全方式設(shè)計(jì)和開發(fā)的數(shù)據(jù)訪問組件如果不以安全的方式進(jìn)行部署,仍然容易受到攻擊。常見的部署做法是使數(shù)據(jù)訪問代碼和數(shù)據(jù)庫駐留在單獨(dú)的服務(wù)器上。這些服務(wù)器通常由內(nèi)部防火墻隔開,這就引進(jìn)了額外的部署注意事項(xiàng)。開發(fā)人員和管理員應(yīng)該了解下列問題:
? |
防火墻限制
|
? |
連接字符串管理
|
? |
登錄帳戶配置
|
? |
登錄審核
|
? |
網(wǎng)絡(luò)上的數(shù)據(jù)私密性和完整性
|
防火墻限制
如果您通過防火墻連接到 SQL Server,請配置防火墻、客戶端和服務(wù)器。可通過使用 SQL Server 客戶端網(wǎng)絡(luò)實(shí)用程序來配置客戶端,并使用服務(wù)器網(wǎng)絡(luò)實(shí)用程序配置數(shù)據(jù)庫服務(wù)器。在默認(rèn)情況下,SQL Server 偵聽 TCP 端口 1433,但您可以更改此設(shè)置。必須在防火墻上打開所選端口。
根據(jù)您所選擇的 SQL Server 身份驗(yàn)證模式以及應(yīng)用程序?qū)Ψ植际绞聞?wù)的使用方式,您可能需要在防火墻上打開幾個(gè)其他端口:
有關(guān)完整配置的詳細(xì)信息,請參閱模塊 18 保證數(shù)據(jù)庫服務(wù)器的安全中的“端口”部分。
連接字符串管理
許多應(yīng)用程序都將連接字符串存儲(chǔ)在代碼中,這主要是為了提高性能。但是,性能優(yōu)勢是可以忽略的,而且使用文件系統(tǒng)緩存有助于確保外部文件中的連接字符串能夠提供相當(dāng)?shù)男阅堋J褂猛獠课募鎯?chǔ)連接字符串對于系統(tǒng)管理極其有益。
為了增加安全性,建議使用 DPAPI 來加密連接字符串。如果您的連接字符串包含用戶名和密碼,這一點(diǎn)尤為重要。然后,確定在何處存儲(chǔ)加密的字符串。注冊表是安全的存儲(chǔ)位置,特別是在您使用 HKEY_CURRENT_USER 時(shí),因?yàn)橹挥性谙嚓P(guān)用戶帳戶下運(yùn)行的進(jìn)程才能進(jìn)行訪問。為了使部署更加容易,還可以將加密的字符串存儲(chǔ)在 Web.config 文件中。這兩種方法已在本模塊前面的配置管理部分討論過。
登錄帳戶配置
一定要讓您的應(yīng)用程序使用最小特權(quán)帳戶來連接到數(shù)據(jù)庫,這是降低 SQL 注入攻擊威脅的主要方法之一。
作為開發(fā)人員,您必須與數(shù)據(jù)庫管理員進(jìn)行協(xié)商,以確定應(yīng)用程序登錄需要訪問的確切的存儲(chǔ)過程和(可能的)表。在理想情況下,您應(yīng)該只允許應(yīng)用程序登錄對隨應(yīng)用程序一起部署的一組有限的存儲(chǔ)過程具有執(zhí)行權(quán)限。
應(yīng)該對 SQL 或 Windows 帳戶、或應(yīng)用程序連接到數(shù)據(jù)庫所使用的帳戶使用強(qiáng)密碼。
有關(guān)針對數(shù)據(jù)庫中的應(yīng)用程序帳戶的建議授權(quán)策略,請參閱本模塊前面的授權(quán)部分。
登錄審核
應(yīng)該將 SQL Server 配置為記錄失敗的登錄嘗試和可能成功的登錄嘗試。審核失敗的登錄嘗試有助于檢測到嘗試發(fā)現(xiàn)帳戶密碼的攻擊者。
有關(guān)如何配置 SQL Server 審核的詳細(xì)信息,請參閱模塊 18 保證數(shù)據(jù)庫服務(wù)器的安全。
網(wǎng)絡(luò)上的數(shù)據(jù)私密性和完整性
如果您使用 SQL 身份驗(yàn)證連接到 SQL Server,請確保不通過網(wǎng)絡(luò)暴露登錄憑據(jù)。可以在數(shù)據(jù)庫服務(wù)器上安裝一個(gè)證書(這會(huì)導(dǎo)致 SQL Server 加密憑據(jù)),或者使用數(shù)據(jù)庫的 IPSec 加密通道。
建議使用數(shù)據(jù)庫的 IPSec 或 SSL 通道來保護(hù)傳入和傳出數(shù)據(jù)庫的敏感的應(yīng)用程序級數(shù)據(jù)。有關(guān)詳細(xì)信息,請參閱模塊 18 保證數(shù)據(jù)庫服務(wù)器的安全。
小結(jié)
本模塊顯示了數(shù)據(jù)訪問代碼的幾種主要威脅,并重點(diǎn)介紹了常見的漏洞。SQL 注入是應(yīng)該注意的主要威脅之一。除非您使用本模塊中討論的正確對策,否則攻擊者會(huì)利用您的數(shù)據(jù)訪問代碼在數(shù)據(jù)庫中運(yùn)行任意命令。傳統(tǒng)的安全措施(如防火墻和 SSL)對 SQL 注入攻擊不提供任何防御功能。您應(yīng)該徹底驗(yàn)證自己的輸入內(nèi)容,并將參數(shù)化存儲(chǔ)過程用作最基本的防御措施。
其他資源
有關(guān)詳細(xì)信息,請參閱下列資源:
? |
有關(guān)可打印的檢查表,請參閱本指南“檢查表”部分中的檢查表:保護(hù)數(shù)據(jù)訪問。
|
? |
有關(guān)保護(hù)開發(fā)人員工作站的詳細(xì)信息,請參閱本指南“如何”部分中的如何:保護(hù)開發(fā)人員工作站。
|
? |
有關(guān)結(jié)合使用 SSL 和 SQL Server 的詳細(xì)信息,請參閱“Microsoft patterns & practices Volume I, Building Secure ASP.NET Web Applications: Authentication, Authorization, and Secure Communication”的“How To”部分中的“How To: Use SSL to Secure Communication with SQL Server 2000”,其網(wǎng)址為:http://msdn.microsoft.com/library/en-us/dnnetsec/html/SecNetHT19.asp(英文)。
|
? |
有關(guān)使用 IPSec 的詳細(xì)信息,請參閱“Microsoft patterns & practices Volume I, Building Secure ASP.NET Web Applications: Authentication, Authorization, and Secure Communication”的“How To”部分中的“How To: Use IPSec to Provide Secure Communication Between Two Servers”,其網(wǎng)址為:http://msdn.microsoft.com/library/en-us/dnnetsec/html/SecNetHT18.asp(英文)。
|
? |
有關(guān)使用 DPAPI 的詳細(xì)信息,請參閱“Microsoft patterns & practices Volume I, Building Secure ASP.NET Web Applications: Authentication, Authorization, and Secure Communication”的“How To”部分中的“How To: Create a DPAPI Library”,其網(wǎng)址為:http://msdn.microsoft.com/library/en-us/dnnetsec/html/SecNetHT07.asp(英文)。
|