原文引自:
http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx
第 5 章 — 安全性考慮事項
發布日期: 8/20/2004 | 更新日期: 8/20/2004
本頁內容
智能客戶端是分布式應用程序,通常跨越多種不同的產品和技術。管理這些應用程序中的安全性是一件極具挑戰性的事情。在服務器端,需要采用一種方法來保護網絡、服務器本身及其應用程序。在客戶端,應集中于利用平臺(其中包括操作系統和 Microsoft? .NET Framework)的安全特性、客戶端代碼可以執行的特權操作(代碼訪問安全)以及與服務器平臺(域)和服務器應用程序的交互。
有效的安全性取決于深層防御方法。在保護智能客戶端時,考慮安全性的各個方面非常重要,其中包括以下幾個方面:
? |
身份驗證。這唯一地標識了客戶端應用程序的用戶,從而只有經過認可的用戶才能訪問應用程序的全部或部分。 |
? |
授權。這確定唯一標識的用戶可以執行的操作。這些操作可以是任務,也可以是對授予經過身份驗證的用戶訪問權限的資源進行的操作。 |
? |
數據驗證。這確保只有適當的和有效的數據才能被應用程序接受。如果允許任何用戶輸入而不首先驗證數據,則攻擊者就可以通過插入惡意的輸入來危及應用程序的安全。 |
? |
保護敏感數據。這意味著確保應用程序存儲和傳輸的敏感數據(例如密碼或機密的商業數據)是安全的。對敏感數據進行加密可以確保數據不可能以明文形式獲得;取決于算法的選擇,這還可以確保信息不會被篡改,從而維護其完整性。 |
? |
審核和日志記錄。這包括保存對事件和用戶操作的記錄。應該考慮將關鍵的用戶操作或活動記錄在服務器上,或者安全地記錄在客戶端上,因為客戶端計算機上的日志可能被篡改或清除。 |
? |
異常管理。這確保應用程序適當地處理異常和失敗,并且返回用戶友好的非敏感信息。異常詳細信息可以記錄到事件日志或應用程序日志中。 |
? |
更改和配置管理。這確保跟蹤 IT 環境的配置以及對其進行的任何更改。通過這樣做,可以查看是否出現任何未經授權的更改,并且確定任何經授權的更改所涉及的安全性含義。 |
本章詳細描述了在設計智能客戶端應用程序時將會面臨的一些關鍵安全性問題,具體來說,重點介紹了身份驗證、授權、數據驗證和保護敏感數據。同時,本章還介紹了代碼訪問安全,它是 .NET Framework 中的一項關鍵技術,用于管理代碼級而非用戶級安全性。
在檢驗智能客戶端安全性時還需要考慮的另一個重要方面是如何部署智能客戶端。有關影響部署的安全性問題的詳細信息,請參閱第 7 章:智能客戶端的部署與更新。
注在應用程序中使用的任何代碼都應該使用 FxCop 進行分析。通過這個工具,可以檢查托管代碼程序集是否符合 .NET Framework 設計指導原則,其中包括符合基本級別的安全性。可以從 GotDotNet 站點 http://www.gotdotnet.com/team/fxcop/ 下載 FxCop。
身份驗證
身份驗證是通過檢驗他的或她的憑據來唯一標識用戶的過程。當用戶試圖運行或安裝應用程序時,或者當應用程序建立到遠程服務的連接或訪問本地保存的數據時,都可能需要用戶身份驗證。
這一節分析智能客戶端常見的一些身份驗證方案,介紹對網絡調用進行身份驗證的幾種不同方式,并討論如何收集用戶憑據和在脫機時檢驗這些憑據。
智能客戶端身份驗證方案
取決于智能客戶端應用程序的樣式和功能,在用戶與應用程序交互的過程中,可能需要在一個或多個階段對用戶進行身份驗證。對用戶進行身份驗證可供選擇的階段有四個:
? |
在應用程序安裝時 |
? |
在應用程序運行時 |
? |
在用戶訪問本地保存的敏感數據時 |
? |
在用戶通過網絡訪問外部服務時 |
經過身份驗證的安裝
如果應用程序是集中部署的(例如,使用無接觸部署),則可能需要保護 Web 服務器上的應用程序,以便只有經過授權的用戶才能安裝它們。這些用戶必須首先經過 Web 服務器的身份驗證,Web 服務器查看他們是否被授權訪問該應用程序和將其下載到他們的客戶端計算機上。
保護對無接觸部署的智能客戶端應用程序的訪問與保護任何其他位于 Web 服務器的構件(例如 Web 頁面)相似。Microsoft Internet Information Services (IIS) 提供了許多身份驗證機制,例如集成 Windows、摘要或基本身份驗證。
注如果使用無接觸部署并且應用程序使用配置文件來存儲它的配置設置,則不適合使用摘要和基本身份驗證,因為 .NET Framework 不能使用這些機制自動下載配置文件。
在選擇適當的身份驗證機制并且 IIS 可以根據他的或她的憑據標識用戶之后,就可以通過設置應用程序和程序集文件上的文件權限來保護應用程序及其依賴程序集。為了簡化對大量用戶的管理,可以考慮提供對 Microsoft Windows 組(例如 SmartClientAppUsers)的訪問,并將各個用戶放在這個組中。
所有需要進行身份驗證的用戶都必須具有服務器上的 Windows 標識,這樣 IIS 就可以保護對應用程序及其程序集的訪問,但是他們未必需要使用這一標識登錄到他們的客戶端計算機上。如果用戶的登錄帳戶不為服務器端所知,則當他或她單擊指向應用程序的可執行文件的鏈接時,系統就會提示用戶輸入用戶名和密碼。
如果使用集成 Windows 身份驗證,則系統就會自動使用已登錄用戶的憑據來嘗試獲取對應用程序的訪問權限。當用戶通過為客戶端和服務器所共有的標識登錄時,就可以進行無縫且安全的訪問。
經過身份驗證的應用程序訪問
對安裝應用程序的用戶進行身份驗證可以確保,只有經過身份驗證和授權的用戶才能從中心位置運行應用程序。然而,因為應用程序及其依賴構件可能緩存在客戶端計算機上,所以不能在應用程序每次運行時都依賴于這一機制來對用戶進行身份驗證。在這種情況下,或者當用戶有意在本地部署應用程序時,就需要仔細考慮如何通過應用程序對用戶進行身份驗證。可能需要在用戶每次運行應用程序時就對他們進行身份驗證,特別是如果應用程序提供敏感功能,或者需要能夠撤消用戶在任何時候運行應用程序的授權。
在用戶使用為客戶端和服務器所共有的標識登錄到客戶端計算機的情況下,可以依賴用戶能夠登錄客戶端計算機的事實,并以此作為對運行應用程序的充分身份驗證。通過這種方法,可以使用 Microsoft Windows 操作系統來提供用戶身份驗證,從而使得不必需要用代碼實現用戶身份驗證。另外,因為 Windows 可以在脫機時緩存用戶憑據,所以您不需要自己緩存它們。
對于您對用戶訪問沒有任何控制的客戶端計算機(例如您的組織的 intranet 外的客戶端計算機),您可能需要采用一種自定義身份驗證機制來收集用戶憑據,并且根據遠程安全性授權對他們進行身份驗證。如果應用程序能夠脫機操作,您就需要在客戶端緩存有效的憑據,這樣,當他或她啟動應用程序時,您就可以根據它們重新對用戶進行身份驗證。您應該定期強制執行聯機重新身份驗證,這樣就可以防止對這樣的應用程序的無限制使用。
經過身份驗證的本地數據訪問
智能客戶端應用程序常常緩存從遠程服務獲得的數據(例如,為了提高響應速度或者為了支持脫機功能)。如果數據是敏感的,則可能需要在授予用戶訪問數據的權限之前對其進行身份驗證。在這種情況下,可以選擇允許未經身份驗證的用戶運行應用程序,但是需要在授予用戶訪問敏感數據的權限之前對其進行充分的身份驗證和授權。
注確保只有授權用戶訪問的數據才能在本地緩存非常重要。如果數據是敏感的,則還需要確保采取足夠的措施來保證數據的安全。詳細信息請參閱本章后面的“處理敏感數據”。本地保存的數據應該保存在安全的位置并進行加密。不管如何對用戶進行身份驗證,通常都需要以某種方式使用他們的憑據來訪問和解密數據。
您可能能夠使用用于登錄到客戶端計算機的默認憑據,也可能需要獲得自定義憑據來根據遠程安全性授權對用戶進行身份驗證。對于在 intranet 環境中運行的應用程序,前一種方式最為合適,而對于在 Internet 或外部網環境中運行的應用程序,由于用戶通常不在與他們訪問的遠程服務相同的域中,所以后一種方式比較合適。使用集成 Windows 身份驗證的一個好處是,操作系統可以對用戶進行身份驗證,保護應用程序和本地數據,并且可以在用戶脫機時緩存用戶憑據。
有關在本地緩存敏感數據的詳細信息,請參閱本章后面的“處理敏感數據”。
經過身份驗證的網絡訪問
您可能會選擇支持對應用程序的匿名訪問并允許任何用戶下載和運行它。然而,在應用程序運行在客戶端之后,它通常需要通過網絡訪問遠程服務(例如 Web 服務)以獲得數據和服務。
通常需要保護對網絡數據和服務的訪問,以防止未經授權的訪問。保護遠程服務訪問的方式可能有許多種,但通常需要將用戶憑據傳送到遠程服務,以便它可以執行用戶身份驗證。
當用戶通過網絡訪問遠程服務時對他們進行身份驗證是一項非常重要的內容。本章后面的“網絡訪問身份驗證類型”比較完整地描述了確保對網絡服務調用進行身份驗證的一些選擇。
選擇正確的身份驗證模型
前一節描述了您可能選擇用來對用戶進行身份驗證的四個階段。您可能選擇在一個或多個階段對用戶進行身份驗證,這取決于您的應用程序及其功能的性質。選擇正確的階段是非常重要的,它既可以幫助確保應用程序和數據的安全,又可以將對應用程序的使用的任何影響降低到最低限度。
如果您的應用程序是集中部署的(例如,如果使用無接觸部署進行部署,或者部署到文件共享),則您可能選擇將訪問僅限于經過授權的用戶。如果您想讓您的應用程序對任何想要使用它的人都可用,則不需要在安裝應用程序時對用戶進行身份驗證。
客戶端計算機通常在物理上并不安全,甚至可能能夠進行公開訪問。如果是這種情況,并且應用程序提供敏感功能,則往往需要在應用程序運行時對用戶進行身份驗證。如果應用程序提供的是匿名用戶可用的常規功能,則不需要在這一階段對用戶進行身份驗證。然而,您可能會選擇給匿名用戶提供應用程序的部分功能,但是需要在允許他們訪問更多受限功能之前對其進行身份驗證。
保護對本地保存的敏感數據的訪問非常重要。如果應用程序部署在物理上不安全或者可以公開訪問的設備上,則應該對敏感數據進行保護,并且只有經過身份驗證和授權的用戶才能訪問它們。應用程序可以給匿名用戶提供常規功能,但是在用戶試圖訪問敏感數據時需要對他們進行身份驗證。
在應用程序脫機運行時,使用集成 Windows 身份驗證也有好處。在這種情況下,Windows 緩存用戶憑據,以便當用戶登錄到脫機客戶端計算機時對他(她)進行身份驗證。如果您需要在應用程序運行或訪問本地保存的敏感數據時對用戶進行身份驗證,則這一行為使得您的客戶端不需要對用戶進行身份驗證。
網絡訪問身份驗證類型
有許多不同的技術可以用來在用戶訪問遠程服務時對其進行身份驗證,其中包括:
? |
集成 Windows 身份驗證。 |
? |
HTTP 基本身份驗證。 |
? |
HTTP 摘要身份驗證。 |
? |
基于證書的身份驗證。 |
? |
基于 Web Services Enhancements (WSE) 的身份驗證。 |
? |
自定義身份驗證。 |
集成 Windows 身份驗證
對于集成 Windows 身份驗證,用戶是通過使用有效的 Windows 帳戶登錄到他的或她的計算機來進行身份驗證的。憑據可以是本地帳戶(對于計算機來說,帳戶是本地的)或域帳戶(Windows 域的有效成員)。在用戶的整個會話期間,應用程序都可以透明地使用登錄時所建立的標識來訪問資源。這意味著應用程序可以用一種安全的方式提供對網絡資源(例如 Web 服務)的無縫訪問,而不必提示用戶輸入其他憑據或重復輸入憑據。
注只有當網絡資源也使用集成 Windows 身份驗證時,對網絡資源的訪問才是無縫的。
集成 Windows 身份驗證使用以下兩種身份驗證協議中的一種:Kerberos 或 NTLM。這兩種技術都不通過網絡傳送用戶名和密碼組合來對用戶進行身份驗證或驗證。因此,您的應用程序或基礎結構在傳輸的過程中不必提供附加的安全性來管理憑據。
依賴于 Windows 安全性的客戶端應用程序使用名為 WindowsIdentity 的 IIdentity 接口的實現。
注.NET Framework 也提供緊密相關的 IPrincipal 接口。有關 IIdentity 和 IPrincipal 接口的更多詳細信息,請參閱本章后面的“授權”。
使用集成 Windows 身份驗證的 Web 服務
對于為集成 Windows 身份驗證配置的 Web 服務,客戶端應用程序可以在調用 Web 服務之前提供當前登錄的用戶的憑據來進行身份驗證。當從 Microsoft Visual Studio .NET 開發系統將引用添加到應用程序中的 Web 服務時,系統會自動生成一個代理類并將其添加到項目中,以便通過編程方式訪問 Web 服務。下面的代碼演示了如何設置當前登錄的用戶的用戶憑據。
MyService service = new MyService(); // A proxy for a web service.
service.Credentials = CredentialCache.DefaultCredentials;
service.SomeServiceMethod(); // Call the web service.
在本例中,DefaultCredentials 使用應用程序正在其中運行的安全性上下文,它通常是運行應用程序的用戶的 Windows 憑據(用戶名、密碼和域)。
HTTP 基本身份驗證
HTTP 基本身份驗證是由 IIS 提供的。在進行基本身份驗證時,IIS 會提示用戶輸入一個有效的 Windows 帳戶和密碼。這一組合是作為經過編碼的明文從客戶端傳送到服務器,并用于在 Web 服務器上對用戶進行身份驗證。
注為了保護基本身份驗證,您需要保護客戶端和服務器之間的通信信道(例如,通過啟用服務器上的安全套接字層 [SSL]),以確保用戶名/密碼組合被加密并且在傳輸時不會被篡改或截獲。同時,您還需要保護位于服務器上的密碼。然而,SSL 只能保護兩個已定義的終結點之間的通信。如果您需要保護兩個以上的終結點之間的通信,則需要使用基于消息的安全性。
使用基本身份驗證的 Web 服務
對于與為基本身份驗證配置的 Web 服務交互的客戶端應用程序,客戶端可以使用登錄對話框來接受有效的用戶憑據,并用它來進行身份驗證。下面的代碼演示了如何為需要基本身份驗證的 Web 服務代理設置用戶憑據。
CredentialCache cache = new CredentialCache();
cache.Add( new Uri( service.Url ), // Web service URL.
"Basic", // Basic Authentication.
new NetworkCredential( userName, password, domain ) );
service.Credentials = cache;
在本例中,userName、password 和 domain 是作為登錄對話框的一部分被接受的。
HTTP 摘要身份驗證
HTTP 摘要身份驗證提供與 HTTP 基本身份驗證相同的功能,但是采用不同的方式來傳輸身份驗證憑據。身份驗證憑據是在一種稱為哈希 的單向處理中進行轉換的。這種處理的結果稱為哈希 或 消息摘要,使用當前的技術不可能對其進行解密。
摘要身份驗證按照以下過程進行:
1. |
服務器給瀏覽器發送將在身份驗證過程中使用的特定信息。 |
2. |
瀏覽器將此信息以及一些其他信息添加到它的用戶名和密碼中,然后對其進行哈希運算。附加信息有助于防止有人復制哈希值并再次使用它。 |
3. |
得到的哈希連同附加信息以明文的方式通過網絡發送到服務器。 |
4. |
服務器將附加信息添加到它所擁有的客戶端密碼的明文副本中,并對所有的信息進行哈希。 |
5. |
服務器將它接收到的哈希值與它剛產生的哈希值進行比較。 |
6. |
只有當兩個值相同時才授予訪問權限。 |
附加信息是在哈希之前添加到密碼中的,這樣就沒有人可以捕獲密碼哈希并利用它來冒充真實客戶端。幫助標識客戶端、客戶端計算機和客戶端所屬的領域或域的值也被添加到密碼中。同時添加的還有時間戳,以防止客戶端在密碼被撤消后還使用密碼。
因為摘要身份驗證以哈希形式通過網絡發送密碼,所以它明顯優于基本身份驗證,特別是在使用基本身份驗證而沒有對通信信道進行加密時。因此,如果可能,您應該使用摘要身份驗證來代替基本身份驗證。
注如同基本身份驗證的情況一樣,只有在向其發出請求的域服務器有請求用戶的密碼的明文副本時,摘要身份驗證才完成。因為域控制器有密碼的明文副本,所以您必須確保該服務器是安全的,不會受到物理和網絡攻擊。
基于證書的身份驗證
通過使用證書,客戶端和服務器應用程序可以使用計算機上安裝的數字密鑰相互進行身份驗證,或建立安全連接。客戶端應用程序可以使用客戶端證書來向服務器標識自己,同樣,服務器也可以使用服務器證書來向客戶端標識自己。一個雙方都信任的第三方(稱為證書頒發機構)可以確認這些證書的標識。客戶端證書可以映射到 Microsoft Active Directory? 目錄服務中的 Microsoft Windows 帳戶。
您可以建立一個站點,使沒有證書的用戶以來賓身份登錄,而有證書的用戶以他的或她的證書所映射的用戶的身份登錄。然后,您可以基于該證書自定義站點。
如果您需要對單個用戶進行身份驗證,您可以使用一種稱為“一對一映射”的技術,這種技術會將證書映射到單個帳戶。如果您需要對來自特定組或組織的所有用戶進行身份驗證,您可以使用多對一映射,例如,它可以將包含共同的公司名稱的所有證書映射到單個帳戶。
在基于證書的身份驗證中,客戶端應用程序使用的是可以由 Web 服務進行身份驗證的證書。在這種情況中,客戶端應用程序使用 X.509 證書來對 SOAP 消息進行數字簽名,以確保消息來自受信任的來源,并且在到達指定的 Web 服務之前沒有在傳輸的過程中被篡改。
基于證書的身份驗證的一個主要考慮事項是如何處理證書將不再有效的情況。例如,如果一個雇員使用證書來進行身份驗證,之后該雇員被解雇了,則此證書應該不再允許該用戶訪問資源。因此,證書安全性基礎結構包括對證書撤消列表的管理非常重要。這些列表放在服務器上,每當客戶端連接到網絡資源時都將進行檢查。
當智能客戶端脫機工作時,不能對基于服務器的撤消列表進行檢查,因此一個不能訪問服務器上的資源的用戶有可能對客戶端上的資源進行本地訪問。為了幫助解決這個問題,您可以選擇相對短的客戶端證書租約時間。短的租約時間會強制客戶端定期連接到證書服務器,并且在續訂租約和允許連接到服務器端應用程序之前確認證書尚未撤消。
有關詳細信息,請參閱 http://www.microsoft.com/resources/documentation/windowsserv/2003/standard/proddocs/en-us/sec_auth_certabout.asp 上的“About Certificates”。
基于 WSE 的身份驗證
使用 Web Services Enhancements 版本 2.0,可以通過編程方式簽署 Web 服務的 SOAP 消息。WSE 2.0 是一個實現,它支持各種新興的 Web 服務標準,例如 WS-Security、WS-SecureConversation、WS-Trust、WS-Policy、WS-Addressing、WS-Referral、WS-Attachments 和 Direct Internet Message Encapsulation (DIME)。WSE 提供一種編程模型來實現它所支持的各種規范。
使用 WSE 的客戶端應用程序可以使用 X509CertificateStore 類中的某種 Find 方法(例如 FindCertificateByHash 或 FindCertificateByKeyIdentifier)通過編程方式從存儲區中選擇一個證書,使用該證書創建數字簽名,將其添加到 WS-Security SOAP 頭并調用 Web 服務。另外,客戶端應用程序還可以選擇打開當前登錄用戶的證書存儲區,如下面的代碼示例所示。
X509CertificateStore store;
store = X509CertificateStore.CurrentUserStore( X509CertificateStore.MyStore );
bool open = store.OpenRead();
有關詳細信息,請參閱 http://msdn.microsoft.com/webservices/building/wse/default.aspx 上的“Web Services Enhancements”。
有關使用客戶端證書的詳細信息,請參閱 WSE 2.0 文檔中的“Signing a SOAP Message Using an X.509 Certificate”。
自定義身份驗證
在某些情況下,Windows 提供的標準身份驗證選擇并不適用于您的應用程序,您將可能需要設計您自己的身份驗證形式。幸運的是,.NET Framework 提供了一些選擇來幫助您設計自定義身份驗證解決方案。
.NET Framework 支持 IIdentity 的一個實現,稱為 GenericIdentity。您可以使用 GenericIdentity,也可以創建您自己的自定義標識類。設計自定義身份驗證解決方案可能比較困難,因為您必須自己采取措施來確保方法是安全的。您可能還需要為您的標識維護單獨的存儲區。
收集和驗證用戶憑據
不管您使用的是什么形式的身份驗證,您都需要收集用戶憑據,然后可以對其進行驗證。對于已經使用集成 Windows 身份驗證登錄的用戶,您可能只需收集現有的憑據,而對于自定義身份驗證解決方案,您可能需要通過您自己的登錄對話框來安全地收集憑據。
注不要將用戶憑據存儲在代碼中超過必要的時間。特別是,不要用全局變量存儲憑據,它們是通過可公開訪問的方法或屬性提供訪問的,另外,也不要將憑據保存在磁盤上。
收集當前登錄的用戶的憑據
如果您使用的是集成 Windows 身份驗證,則您的用戶是在他們的 Windows 會話開始時登錄的。然后,您的應用程序可以使用這一信息來確保他們具有適當的憑據用于運行。
下面的代碼演示了基本功能。
using System.Security.Principal;
// Get principal of the currently logged in user.
WindowsPrincipal wp = new WindowsPrincipal( WindowsIdentity.GetCurrent() );
// Display the current user name.
label1.Text = "User:" + wp.Identity.Name;
// Determine if user is part of a windows group.
if( wp.IsInRole( "YourDomain\\YourWindowsGroup" ) )
{
// Is a member.
}
else
{
// Is not a member.
}
使用登錄對話框收集用戶憑據
如果您設計您自己的登錄對話框接受來自用戶的憑據,則需要采取大量的措施來確保您滿足您的組織的安全性要求(例如強制執行強密碼策略和在定期的時間間隔內讓密碼過期)。在設計登錄對話框時,請考慮使用下列指導原則:
? |
不要盲目信任用戶輸入。如果您盲目信任用戶輸入,惡意用戶就可能危及您的應用程序的安全。例如,如果不對動態構造 SQL 代碼進行驗證,使用輸入的應用程序就容易受到 SQL 插入攻擊。 |
? |
檢查輸入數據的類型、格式或范圍。考慮使用正則表達式來進行這些檢查。使用正則表達式可以檢查類型(例如,字符串或整型)、格式(例如,強制密碼策略要求使用諸如數字、特殊符號及大小寫字母字符混合等)和范圍(例如,用戶名稱最少包含 6 個字符,最多包含 25 個字符)。
下面的代碼示例強制要求密碼長度為 8 至 10 個字符,并且是大小寫字母和數字字符的組合。 // Validate the user supplied password.
if( !Regex.Match( textBox1.Text,
@"^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$",
RegexOptions.None ).Success )
{
// Invalid email address.
}
|
? |
在設計帶有密碼字段文本框的對話框時,請確保 PasswordChar 屬性設置為當文本輸入控件時顯示的字符,如下面的示例所示。 // The password character is set to asterisk.
textBox1.PasswordChar = ?*';
|
身份驗證指導原則
在為應用程序設計身份驗證時,應該考慮使用下列指導原則:
? |
確定在用戶與應用程序交互時什么階段需要進行身份驗證。 |
? |
考慮在用戶登錄到客戶端時使用集成 Windows 身份驗證對他們進行身份驗證,之后用戶就可以訪問應用程序、應用程序的數據和任何遠程服務。 |
? |
如果應用程序是集中部署的并且需要將訪問僅限于經過授權的用戶,就應該在應用程序運行時使用 IIS 提供的身份驗證機制之一對用戶進行身份驗證。 |
? |
如果應用程序提供敏感功能或者提供對本地保存的敏感數據的訪問,則應該確保在允許用戶訪問之前對他們進行適當的身份驗證。 |
? |
如果應用程序需要自定義身份驗證,則應該確保應用程序強制執行強用戶名和密碼策略。作為慣例,應該需要最少 8 個字符和大小寫字母字符、數字和特殊字符的混合。 |
? |
如果遠程服務提供敏感功能或者提供對敏感數據的訪問,則需要對用戶進行身份驗證,以便他們通過網絡訪問遠程服務。 |
? |
確保用戶憑據在沒有保護措施的情況下不通過網絡進行傳輸。有些身份驗證形式根本不通過網絡傳送用戶憑據,但是如果必須傳送,就應該確保它們經過加密,或者通過安全連接進行傳送。 |
有關詳細信息,請參閱 Improving Web Application Security: Threats and Countermeasures 的“Chapter 4 — Design Guidelines for Secure Web Applications”中的“身份驗證”,地址在 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/THCMCh04.asp。
授權
在對用戶進行身份驗證之后,可以通過授權來確定他們有權訪問系統內的什么內容。授權是確認經過身份驗證的用戶具有執行某種操作的權限的過程。授權管理經過身份驗證的用戶可以訪問的資源(例如,文件和數據庫)和可以執行的操作(例如,更改密碼或刪除文件)。沒有經過身份驗證的用戶(即匿名用戶)不能進行特別授權,而是需要分配一組默認權限。
許多因素嚴格地確定了您可以如何在您的環境中執行授權。您需要確定是基于應用程序的功能還是基于系統資源來管理授權。您需要確定是執行方法內的細粒度授權還是執行方法級的檢查。此外,您還需要確定在何處存儲授權所需的用戶信息(例如,在 Active Directory 或 Microsoft SQL Server 數據庫中)。如果您將允許您的智能客戶端能夠脫機工作,則需要有對脫機客戶端進行授權的策略。
.NET Framework 提供 IPrincipal 接口,它可以與 IIdentity 接口一起,用于定義管理運行代碼的安全上下文的屬性和方法。同時提供的還有該接口的兩個實現:WindowsPrincipal 和 GenericPrincipal。使用集成 Windows 身份驗證的客戶端應用程序使用的是 WindowsPrincipal,而使用自定義身份驗證的客戶端應用程序使用的是 GenericPrincipal。
授權的類型
Windows 操作系統中常用的授權方法有兩種:基于資源的授權和基于角色的授權。基于資源的授權依賴于訪問控制列表 (ACL),而基于角色的授權則基于用戶角色執行授權。
基于資源的授權
對于基于資源的授權,可以將任意訪問控制列表 (DACL) 附加到保險對象。然后,系統作出訪問決定,方法是比較令牌中的組成員身份與 ACL 的內容,以確定該用戶是否具有所請求的訪問權限。對于許多類型的應用程序,ACL 模型是非常理想的。然而,它并非適合于所有的情況。例如,您可能需要基于業務邏輯或者基于在需要時創建的非持久性對象來作出訪問決定。
基于角色的授權
基于角色的授權允許您將用戶和組與他們完成其工作所需的權限相關聯。當將一個用戶或組添加到一個角色中時,該用戶或組就會自動地繼承各種安全權限。這些權限可以是執行操作的權限,也可以是訪問各種資源的權限。圖 5.1 說明了基于角色的授權中角色與權限之間的關系。
圖 5.1 基于角色的授權
在 Microsoft Windows 2000 Server Service Pack 4 (SP4) 和 Windows Server 2003 操作系統中,基于角色的授權通常是通過授權管理器管理的。授權管理器是一組基于 COM 的運行時接口、以及用于配置的 Microsoft 管理控制臺 (MMC) 管理單元。開發人員可以使用授權管理器確保應用程序能夠管理和驗證客戶端對執行應用程序操作的請求,而應用程序管理員則可以用它來管理用戶角色和權限。通過使用授權管理器,可以將低級操作聚集成稱為“Tasks”的組,并管理這一級別的授權。同時它還使得您能夠在授權前后運行自定義授權邏輯。
授權管理器的一個顯著優點是,它可以從需要授權的應用程序中進一步抽象授權存儲,這意味著開發人員可以一直與授權管理器通信,而不管存儲是在 Active Directory 中還是基于文件的。
給應用程序添加授權功能
.NET Framework 提供了許多選擇來給應用程序添加授權功能。這些選擇包括:
? |
使用 PrincipalPermissionAttribute 執行聲明性 Demand。 |
? |
使用 PrincipalPermission 對象執行命令性 Demand。 |
? |
使用 IsInRole 方法執行角色檢查。 |
? |
執行用于自定義身份驗證的角色檢查。 |
使用 PrincipalPermissionAttribute 執行聲明性 Demand
可以將 Demand 放在類級或成員級的單個方法、屬性或事件上。如果將聲明性 Demand 同時放在類級和成員級上,則成員級的聲明性 Demand 會重寫(或替換)類級的 Demand。
下面的代碼示例顯示了 PrincipalPermission 對象的聲明性 Demand。
// Declarative example.
[PrincipalPermissionAttribute( SecurityAction.Demand, Role="Teller" )]
void SomeTellerOnlyMethod()
{
}
使用 PrincipalPermission 對象執行命令性 Demand
可以執行命令性 Demand,方法是通過編程方式調用 PrincipalPermission 對象的 Demand 方法,如下面的代碼示例所示。
// Programmatic example.
public SomeMethod()
{
PrincipalPermission permCheck = new PrincipalPermission( null, "Teller" );
permCheck.Demand();
// Only Tellers can execute the following code.
// Non members of the Teller role result in a security exception.
. . .
}
通過編程方式調用該方法的一個優點是,可以確定主體是否在多個角色中。.NET Framework 不允許以聲明的方式這樣做。下面的代碼示例顯示了如何執行這一檢查。
// Using PrincipalPermission.
PrincipalPermission permCheckTellers = new PrincipalPermission( null,
"Teller" );
permCheckTellers.Demand();
PrincipalPermission permCheckMgr = new PrincipalPermission( null,
"Manager" );
permCheckMgr.Demand();
使用 IsInRole 方法執行角色檢查
可以選擇直接訪問主體對象的值并執行檢查,而不使用 PrincipalPermission 對象。在這種情況下,可以讀取當前線程的主體的值,也可以使用 IsInRole 方法來執行授權,如下面的代碼示例所示。
// Using IsInRole.
if ( Thread.CurrentPrincipal.IsInRole( "Teller" ) &&
Thread.CurrentPrincipal.IsInRole( "Manager" ) )
{
// Perform privileged operation.
}
執行用于自定義身份驗證的角色檢查
如果應用程序不是基于 Windows 的,則可以通過編程方式將一組從自定義身份驗證數據存儲區(例如 SQL Server 數據庫)中獲得的角色填充 GenericPrincipal 對象,如下面的代碼示例所示。
GenericIdentity userIdentity = new GenericIdentity( "Bob" );
// Typically role names would be retrieved from a custom data store.
string[] roles = new String[]{ "Manager", "Teller" };
GenericPrincipal userPrincipal = new GenericPrincipal( userIdentity, roles );
if ( userPrincipal.IsInRole( "Teller" ) )
{
// Perform privileged operation.
}
授權指導原則
授權對控制用戶訪問應用程序功能和資源的權限非常關鍵。不適當的或弱的授權會導致信息泄露和數據篡改。請考慮使用下列指導原則:
? |
盡可能地使用多個網關守衛以在用戶訪問資源或執行操作時強制執行授權檢查。使用客戶端檢查和服務器上的檢查相結合的方法來提供深層防御,以防止設法繞開其中一個網關守衛的惡意用戶的攻擊。服務器上常見的網關守衛包括 IIS Web 權限、NTFS 文件系統權限、Web 服務文件授權(只適用于 Windows 身份驗證)和主體權限 Demand。 |
? |
使用用戶的安全上下文授權訪問系統資源。可以使用基于角色的授權來基于用戶標識和角色成員身份授權訪問。集成 Windows 身份驗證與安全資源(例如文件或注冊表)上的 Windows ACL 共同確定是否允許調用者訪問資源。對于程序集,可以基于諸如程序集的強名稱或位置等證據來授權調用代碼。 |
? |
確保使用足夠的粒度來定義角色,以便充分分離特權。避免只為滿足某些用戶的要求而授予提高的特權;相反,考慮添加新的角色來滿足這些要求。 |
? |
盡可能地使用聲明性 Demand 而非命令性 Demand。聲明性 Demand 提供或拒絕對方法的全部功能的訪問。另外,它們還可以更好地與安全工具一起使用,并且有助于進行安全審核,因為這些工具能夠通過檢驗應用程序來訪問這些 Demand。 |
? |
如果您需要確定主體是否在多個角色中,則可以考慮使用 IsInRole 方法進行命令性檢查。.NET Framework 版本 1.1 不允許以聲明的方式執行 AND 檢查;然而,可以通過編程的方式在方法內執行它們,如下面的代碼示例所示。 // Checking for multiple roles.
if ( Thread.CurrentPrincipal.IsInRole( "Teller" ) &&
Thread.CurrentPrincipal.IsInRole( "Manager" ) )
{
// Perform privileged operation.
}
|
? |
使用代碼訪問安全來授權調用對特權資源或操作的代碼訪問(基于諸如程序集的強名稱或位置這樣的證據)。有關詳細信息,請參閱本章后面的“代碼訪問安全”。 |
客戶端脫機時授權使用功能
當用戶連接到網絡時,可以直接根據網絡授權存儲對他們進行授權;但是當他們沒有連接到網絡時,仍然可能需要對他們進行授權。
任何形式的授權都不會比所使用的身份驗證機制更強。如果您允許匿名身份驗證,您就應該特別注意允許用戶訪問什么功能,并且通常不應該授權用戶訪問系統資源。
如果您授權用戶使用應用程序,則可以讓 Windows 擔當唯一的網關守衛來確定哪些資源可用于已登錄用戶的配置文件。在這種情況下,通常只允許用戶訪問本地系統資源。
您可以選擇為不同的角色創建同一個應用程序的不同版本。當用戶連接到網絡時,他或她只被允許安裝與其角色相符的應用程序版本。然后,當用戶脫機運行應用程序時,無需為應用程序建立連接即可自動提供正確的功能。
授權和配置文件應用程序塊
Microsoft 提供了一個應用程序塊,它能夠提供基礎結構來簡化在應用程序中加入授權功能的過程。
授權和配置文件應用程序塊為基于角色的授權和對配置文件信息的訪問提供基礎結構。該應用程序塊允許您執行下列操作:
? |
對應用程序或系統的用戶進行授權。 |
? |
使用多個授權存儲提供程序。 |
? |
插入操作驗證的業務規則。 |
? |
將多個標識映射到單個用戶,從而將標識的概念擴展到包括身份驗證方式。 |
? |
訪問可以存儲在多個配置文件存儲區中的配置文件信息。 |
有關詳細信息,請參閱 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/authpro.asp 上的 Authorization and Profile Application Block。
輸入驗證
輸入驗證不嚴格的應用程序可能會受到攻擊者惡意輸入的威脅。驗證用戶輸入是保護應用程序的最前沿防線中的一道防線。請考慮使用下面針對智能客戶端應用程序的輸入驗證指導原則:
? |
確保智能客戶端應用程序在處理輸入或將輸入傳送到下游資源和程序集之前驗證了所有的輸入。 |
? |
如果您要將輸入數據傳送給非托管 API,請對用戶輸入數據進行徹底的驗證。這樣做有助于防止緩沖區溢出。您還應該限制用戶輸入傳送到非托管 API 的數據。 |
? |
始終驗證從所有外部來源(例如 Web 站點和 Web 服務)獲得的數據。 |
? |
切勿依賴對傳送到 Web 服務器或者 Web 應用程序的數據進行的客戶端驗證。首先在客戶端驗證數據,然后再次在服務器上驗證數據,以便防止繞過客戶端驗證的惡意輸入。 |
? |
切勿允許用戶直接輸入 SQL 查詢。始終提供針對安全問題進行了徹底的檢查的預先打包的或參數化查詢。允許用戶直接輸入 SQL 查詢可能帶來 SQL 插入攻擊。 |
? |
用已知的正確值或模式(而不是用錯誤的輸入)來約束并驗證用戶輸入。檢查一個有限已知值的列表比檢查一個無限未知惡意輸入類型的列表簡單得多。在對輸入值采取行動之前,既可以拒絕有害的輸入,也可以對輸入進行凈化(即除去潛在的不安全字符)。 |
? |
通過驗證輸入的類型、長度、格式和范圍來約束它。一種這樣做的方法就是使用正則表達式(可以從 System.Text.RegularExpressions 命名空間中獲得)來驗證用戶輸入。 |
? |
拒絕未知的有害數據,然后凈化輸入。如果應用程序需要接受某些自由格式的用戶輸入(例如,文本框中的注釋),可以對輸入進行凈化,如下面的示例所示: private string SanitizeInput( string input )
{
// Example list of characters to remove from input.
Regex badCharReplace = new Regex( @"([<>""?%;()&])" );
string goodChars = badCharReplace.Replace( input, "" );
return goodChars;
}
|
? |
考慮集中驗證例程,以減少開發工作量并有助于未來的維護。 |
有關詳細信息,請參閱 Improving Web Application Security: Threats and Countermeasures 的“Chapter 4 — Design Guidelines for Secure Web Applications”中的“輸入驗證”,地址在 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/THCMCh04.asp。
處理敏感數據
如果您經常設計 Web 應用程序,您就會理解保護存儲的數據和傳輸的數據的重要性。存儲在 Web 服務器上的數據通常被寫入物理上安全的位置,這樣的位置已經很好地被保護起來以防止受到攻擊。在智能客戶端應用程序中,您同樣需要仔細考慮駐留在客戶端上的數據。如果這些數據是敏感的,適當地對其進行處理以確保安全就非常重要。為了保護傳輸的數據,您可以使用 SSL 保護傳輸層,并且使用 WS-Security 或 Message Queuing 加密工具保護消息內容。
只有授權用戶訪問的數據才應該可用于客戶端應用程序。如果一臺計算機上的多個人都可以使用客戶端應用程序,則與每個單獨的用戶相關的數據都應該被認為是敏感數據,并且應該采取一些措施來確保只有經過授權的用戶可以訪問它。
敏感數據包括攻擊者可能發現對訪問或修改有用的任何數據,因為這些信息是機密的或者對攻擊有幫助。敏感數據可以是服務器提供給客戶端的數據,但也可以包括應用程序配置文件、本地數據庫或注冊表信息。
在通常情況下,應該盡力確保敏感數據不本地緩存。然而,在智能客戶端應用程序的情況下,可能需要緩存這些數據(例如,為允許偶爾連接的操作,將數據保存在本地存儲區以供日后使用)。
注在某些情況下,敏感數據可能由于內存中的分頁而發送到磁盤上。因此,在確定需要對什么樣的數據進行加密時,也應該考慮存在于內存中的數據。
確定將哪些數據存儲在客戶端上
根據定義,用戶(因而是潛在的攻擊者)可以在物理上接觸客戶端。如果給予足夠的時間,攻擊者常常能夠獲得足夠的管理權限來訪問幾乎任何數據,因此您應該仔細考慮應該將什么樣的數據存放在客戶端。作為一般的規則,您應該對服務器進行授權決策,這樣,就只有您從服務器傳送到客戶端的數據是允許用戶訪問的數據。除了提高性能之外,對服務器進行授權決策也可以確保這種數據不會被客戶端上的潛在攻擊者訪問得到。
您應該從不將敏感數據存儲在基于文本的文件中,并且應該始終對這些數據進行加密,這樣就只有經過授權的用戶才可以輕松地進行訪問。您應該避免使用基于文本的配置文件來存儲敏感的安全信息,例如密碼或數據庫連接字符串。如果這些信息必須在本地存儲,您就應該對信息進行加密,并將其存儲在文件或注冊表項中,然后使用 DACL 來限制對該對象的訪問。此外,還必須保護針對登錄用戶個人的任何永久性數據的隱私和安全,特別是當計算機在用戶之間共享時。
在許多情況下,如果應用程序需要脫機運行,則較多的數據就會存儲在客戶端上。然而,您應該確定脫機時是否需要所有的數據,或者是否您想要限制用戶在脫機時執行某些操作,這樣您就不必將敏感數據緩存在本地。
在某些情況下,如果數據是機密的,并且可以按需要由用戶輸入,則可以選擇根本不將數據本地存儲在客戶端上,而是在需要時從用戶獲得。
如果應用程序需要在本地存儲敏感數據,通常應該避免使用可移動存儲器(例如軟盤、zip 磁盤、或 USB 存儲設備)或外部便攜式存儲器來存儲敏感數據。然而,當可以確保可移動媒體為該用戶所有(例如,通過使用證書或智能卡)時,就可以將特定于用戶的數據存儲在可移動媒體中。因此,可以將特定于用戶的數據存放在隨用戶一起移動的安全位置,這樣,漫游用戶就可以在不將數據保留在本地計算機上的情況下訪問應用程序及其數據。
注當考慮將哪些敏感數據存儲在客戶端上時,您應該確保存儲關于雇員或顧客的信息沒有違反隱私法規。這些法律因國家/地區而異,因此您應該熟悉使用您的應用程序的國家/地區的隱私法規。
保護敏感數據的技術
對于需要存儲在客戶端上的數據,有許多可以采取的附加措施來防止未經授權的訪問。這些措施包括下列內容:
? |
確保只有經過授權的用戶才可以訪問數據。 |
? |
考慮使用 EFS 加密文件。 |
? |
考慮使用 DPAPI 避免密鑰管理問題。 |
? |
考慮存儲哈希值而不是明文。 |
? |
考慮部分受信任的應用程序的隔離存儲。 |
? |
保護私鑰。 |
確保只有經過授權的用戶才可以訪問數據
數據常常需要進行保護,以幫助確保只有經過授權的用戶才可以訪問。取決于應用程序的性質和數據保持有效的時間,可以選擇使用基于資源的安全性或基于角色的安全性來保護數據。有關詳細信息,請參閱本章前面的“授權指導原則”。
考慮使用 EFS 加密文件
確保將文件安全地存放在智能客戶端上的一種方法是使用加密文件系統 (Encrypting File System, EFS) 對敏感數據文件進行加密。這個解決方案在可擴展方面乏善可陳;然而,它對某些特定的文件非常有用,它也可以用于在客戶端上本地緩存數據的情況(例如,用于啟用偶爾連接的智能客戶端)。
考慮使用 DPAPI 避免密鑰管理問題
Windows 2000 和 Windows 操作系統的更高版本提供了用于加密和解密數據的 Win32? 數據保護 API (DPAPI)。它是加密 API (Crypto API) 的一部分,并且是用 crypt32.dll 實現的。它包含兩個方法:CryptProtectData 和 CryptUnprotectData。
DPAPI 特別有用,因為它可以消除暴露給使用密碼的應用程序的密鑰管理問題。雖然加密可以確保數據是安全的,但是您必須采取額外的步驟來確保密鑰的安全。為了派生加密密鑰,DPAPI 使用與調用 DPAPI 函數的代碼相關的用戶帳戶的密碼。因此,是由操作系統(而不是由應用程序)管理密鑰。
DPAPI 可以與機器存儲區或用戶存儲區一起工作。用戶存儲區是基于登錄用戶的配置文件自動加載的。客戶端應用程序通常與用戶存儲區一起使用 DPAPI,除非需要存儲為所有可以登錄到計算機的用戶共有的機密。
DPAPI 用來加密和解密敏感數據的密鑰是特定于計算機的。系統會為每臺計算機生成一個不同的密鑰,這可以防止一個服務器能夠訪問由另一個服務器加密的數據。
非托管 DPAPI 需要程序集具有完全的信任。具有完全和部分受信任的程序集的應用程序可以使用高特權來分離代碼,并且使其能夠從部分受信任的代碼中進行調用。有關更多信息,請參閱 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secmod/html/secmod115.asp 上的“How To Create a Custom Encryption Permission”。
考慮存儲哈希值而不是明文
有時,存儲數據是為了使之可以用于驗證用戶輸入(例如,用戶名和密碼的組合)。在這樣的情況下,可以存儲數據的加密哈希,而不是以明文的形式存儲數據本身。然后,當用戶進行輸入時,還可以對該數據進行哈希運算,并且可以比較兩個哈希。存儲哈希減少了機密被發現的風險,因為從其哈希推導原始數據或從其他數據生成相同的哈希在計算上是不可能的。
考慮部分受信任的應用程序的隔離存儲
隔離存儲允許應用程序將數據保存到唯一的數據艙,數據艙與代碼的標識的某些方面相關聯,例如它的 Web 站點、發布者或簽名。數據艙是一個抽象的概念,而不是一個具體的存儲位置;它由一個或多個隔離的存儲文件(稱為存儲)組成,存儲包含存儲數據的實際目錄位置。
對于需要存儲特定于特定用戶和程序集的狀態數據的部分受信任的應用程序,隔離存儲特別有用。部分受信任的應用程序并不直接訪問文件系統來保持狀態,除非通過更改安全策略顯式地授予它們這樣做的權限。
存儲在隔離存儲中的數據是隔離的,并且受到保護,不會被其他部分受信任的應用程序訪問,但是無法保護它不會被完全受信任的代碼或有權訪問客戶端計算機的其他用戶訪問。為了在這些情況下保護數據,您應該通過使用 DACL 采用數據加密或者文件系統安全性。有關詳細信息,請參閱 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconIntroductionToIsolatedStorage.asp 上的 .NET Framework Developer's Guide 中的“Introduction to Isolated Storage”。
保護私鑰
未受保護的私鑰易于受到惡意用戶或惡意代碼進行的各種各樣的攻擊。用于簽署程序集的私鑰不應該放在不安全的位置,例如開發人員的計算機或開放共享的環境。攻擊者可以使用竊取的私鑰簽署帶有您的強名稱的惡意代碼。您確實應該通過您的組織內為此目的而指定的中央安全機構來保護您的私鑰。您也可以將私鑰保存在物理上安全的隔離計算機上,并且在必要時使用便攜式媒體來傳遞密鑰。
有關有效地存儲機密的詳細信息,請參閱 Michael Howard 和 David LeBlanc 撰寫的 Writing Secure Code, Second Edition。
代碼訪問安全
代碼訪問安全是 .NET Framework 技術,它將身份驗證和授權原則應用于代碼而不是用戶。代碼訪問安全是一種強大的機制,它確保只有用于運行的代碼才可以被用戶運行。
所有的托管代碼都遵循代碼訪問安全機制。當加載一個程序集時,會授予它一組代碼訪問權限,以確定它可以訪問什么資源和執行什么類型的特權操作。為了授予這些權限,.NET Framework 安全系統使用證據來對代碼進行身份驗證(標識)。
注 程序集是代碼訪問安全的配置和信任單元。相同程序集中的所有代碼都接收相同的權限,因而是同等受信任的。
代碼訪問安全包括下列要素:
? |
權限。權限代表代碼訪問安全資源或執行特權操作的權力。Microsoft .NET Framework 提供代碼訪問權限 和代碼標識權限。代碼訪問權限封裝訪問特特定資源或者執行特定特權操作的能力。例如,在應用程序可以執行任何文件的 I/O 操作之前都需要 FileIOPermission。代碼標識權限用來基于調用代碼的標識的某個方面(例如,代碼的強名稱)限制對代碼的訪問。 |
? |
權限集。.NET Framework 定義了許多權限集,它們代表一組通常作為一個整體分配的權限。例如,.NET Framework 定義的 FullTrust 權限集將所有的權限分配給完全受信任的代碼;而 LocalIntranet 權限集則分配數量非常有限的權限。 |
? |
證據。.NET Framework 安全系統使用證據來標識程序集。代碼訪問安全策略使用證據來幫助將恰當的權限授予恰當的程序集。證據可以是位置相關的(例如,URL、站點、應用程序目錄或區域),也可以是作者相關的(例如,強名稱、發布者或哈希)。 |
? |
策略。代碼訪問安全策略是由管理員配置的,它確定授予程序集的權限。策略可以建立在企業、機器、用戶和應用程序域級別上。每個策略都用一個 XML 配置文件定義。 |
? |
代碼組。每個策略文件都包含一些分層代碼組。代碼組用來給程序集分配權限。代碼組由成員身份條件(基于證據)和權限集組成。.NET Framework 定義了許多默認的代碼組,例如 Internet、本地 Intranet、受限制的和受信任的區域。 |
有關代碼訪問安全的詳細信息,請參閱 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/THCMCh07.asp 上的 Improving Web Application Security: Threats and countermeasures:“Chapter 7 — Building Secure Assemblies”和 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/THCMCh08.asp 上的“Chapter 8 — Code Access Security in Practice”。
代碼訪問安全權限解析
代碼訪問安全使用圖 5.2 中概述的步驟來確定將哪些權限分配給程序集。
圖 5.2 確定將哪些權限分配給程序集
下面的步驟更詳細地描述了這些過程:
1. |
加載程序集,收集證據并將其提供給主機。 |
2. |
根據用于宿主環境的安全策略來評估證據。 |
3. |
這種評估的輸出是一組授予程序集的權限。這些權限定義了在此環境中程序集能夠做什么和不能做什么。 |
4. |
當程序集請求執行特權操作時,系統就會將操作的請求與程序集的權限進行比較。如果程序集具有這樣的權限,就允許代碼執行該操作,否則,引發安全異常。 |
代碼訪問安全設計
分配給代碼的權限取決于與代碼相關的證據和客戶端計算機上的適當安全策略。為了在保持應用程序的功能的同時確保其安全,您需要仔細考慮應用程序所需的權限、以及授予這些權限的方式。
授予所有權限的應用程序(這些應用程序定義在 FullTrust 權限集中)稱為完全受信任的應用程序。沒有授予所有權限的應用程序稱為部分受信任的應用程序。
在理論上,將應用程序設計為部分受信任通常更可取。然而,智能客戶端應用程序常常需要執行許多部分受信任的應用程序在默認情況下不能執行的操作。這些操作包括:
? |
訪問運行應用程序的服務器以外的服務器,或者訪問使用不同協議的服務器。 |
? |
訪問本地文件系統。 |
? |
訪問本地 Microsoft Office 應用程序并與其交互。 |
? |
訪問并與非托管代碼(例如 COM 對象)交互。 |
如果需要智能客戶端執行這些種類的操作,則應該考慮使它成為一個完全受信任的應用程序,或者授予它正確地進行操作所需的其他特定權限。
注在默認情況下,使用無接觸部署方式部署的應用程序自動成為部分受信任的。如果智能客戶端需要執行部分受信任的應用程序不能執行的額外操作,則需要部署新的安全策略或者使用其他方法來部署應用程序。
設計和構建部分受信任的應用程序可能比較復雜,但是仔細考慮并限制授予應用程序的權限有助于確保應用程序不能執行不恰當的操作或者訪問明顯不需要的資源。
所有的代碼在運行之前都必須給其授予運行的權限,但是訪問安全資源或者執行其他安全敏感操作(例如調用非托管代碼或訪問本地文件系統)的代碼必須由 .NET Framework 授予額外的權限才能運行。這樣的代碼稱為特權代碼。相反,非特權代碼并不需要訪問敏感資源,而只是需要運行的權限。當設計和構建應用程序及其程序集時,應該標識特權和非特權代碼并將其存檔。這樣做有助于確定代碼需要的權限。
還應該仔細檢驗 .NET Framework 使用哪些證據來給代碼分配權限。只有在中心位置安全的情況下才應該考慮基于應用程序的位置(例如,文件共享或 Web 服務器)的證據。類似地,只有在密鑰安全的情況下才應該使用具有基于一般密鑰 — 用于簽署所有的代碼(例如,由組織的 IT 部門)— 的證據的應用程序。然而,依賴于強名稱證據而不是任何基于位置的證據(例如 Web 服務器地址)通常更安全一些。
設計部分受信任的應用程序
當設計部分受信任的應用程序時,請遵循下列指導原則:
? |
了解應用程序的部署方案。 |
? |
避免引起異常的權限請求。 |
? |
對部分受信任的調用程序使用 Demand/Assert 模式。 |
? |
考慮對程序集使用強名稱。 |
? |
避免給受限區域賦予完全的信任。 |
了解應用程序的部署方案
在設計期間應該清楚地了解應用程序的部署方案,因為部署應用程序的位置對默認授予應用程序的權限有很大的影響。諸如顯示對話框(例如,使用 SaveFileDialog)或訪問系統資源的應用程序功能都可能因為應用程序的部署位置而受到限制。
特別地,授予應用程序的權限取決于它所在的區域(例如,Internet 區域、本地 Intranet 區域或受信任的區域)。用戶可以對受信任的區域內的應用程序的成員身份有一定的控制,但是不應該依賴用戶將應用程序放在此區域來確保正確的功能。您應該這樣設計您的應用程序,使之在運行時被授予的權限不夠時會正常地失敗。
如果用戶嘗試執行某種操作,而應用程序卻沒有足夠的權限來執行該操作,則這種嘗試可能會導致失敗的權限請求,這又會引發安全異常。應用程序需要處理這些異常,否則它就會出現故障。您應該確保可以很好地處理這樣的故障,并且應該給用戶提供足夠的信息來解決問題,而同時不泄露與安全有關的不適當的或敏感的信息。
注系統可以按照使用 .NET Framework 2.0 版本的 ClickOnce 部署功能部署的應用程序的部署清單授予它們特定的權限。在部署應用程序時,授予的權限將固定,而且將應用程序的位置放在受信任的區域中不會影響授予的權限。
避免引起異常的權限請求
確定每個應用程序功能在不引起異常的情況下正確地運行所需的權限。請考慮使用如下措施:
? |
設計一個替代解決方案來避免可能引起異常的權限請求。例如,對于基于 Intranet 的應用程序,您可以使用 OpenFileDialog 來顯示一個對話框,指示用戶選擇文件,而不要讓應用程序自動從硬盤打開并讀取文件。 |
? |
適當地處理異常的檢查權限(特別是 SecurityException)。在您的代碼中,您可以創建一個特定于您正試圖訪問的資源的權限類的實例,并且在訪問資源之前檢查必要的權限。例如,當您必須使用 OpenFileDialog 或 SaveFileDialog 顯示對話框時,可以使用 FileDialogPermission 類和 SecurityManager.IsGranted 靜態方法來檢查權限,如下所示。 FileDialogPermission fileDialogPermission = new
FileDialogPermission( FileDialogPermissionAccess.Save );
if ( !SecurityManager.IsGranted( fileDialogPermission ) )
{
// Not allowed to save file.
}
注IsGranted 并不保證操作將成功,因為它沒有遍歷堆棧來確定是否所有的上游代碼都具有必需的權限。 |
? |
考慮原型設計并對各種部署區域測試您的應用程序方案:
? |
如果您的應用程序設計成能從文件共享運行,您可以將該應用程序的地址設置為網絡共享(例如,\\MachineName\c$\YourAppPath\YourApp.exe)并從您的硬盤運行它來模擬這種方案。 |
? |
如果您的應用程序設計成是從 Web Internet 區域運行,您可以使用您的計算機的 IP 地址(例如,\\<MachineIPaddress\c$\YourAppPath\YourApp.exe)來模擬這種方案。 | |
對部分受信任的調用程序使用 Demand/Assert 模式
Demand/Assert 模式用于完全受信任的程序集中,以允許部分受信任的調用程序在調用時訪問特權操作。當部分受信任的調用程序需要以安全的方式執行特權操作而又不具有必要的特權時,這種模式非常有用。通過使用 Assert,您可以擔保您的代碼的調用程序是值得信任的。
注只有當您完全理解它的使用可能帶來的安全風險時,才能使用 Demand/Assert 模式。斷言權限關閉了正常的 .NET Framework 權限檢查,這種權限檢查會檢查堆棧中所有的調用代碼。關閉這種機制可能會將嚴重的安全漏洞引入代碼中,因此只有在您完全理解該模式的含義并用盡所有其他可能的解決方案時,才能嘗試這種模式。
在這種模式中,Demand 調用發生在 Assert 調用之前。Demand 查看調用程序是否具有該權限,然后,Assert 關閉特定權限的堆棧遍歷,這樣公共語言運行庫就不會檢查調用程序是否具有適當的權限。
為了讓部分受信任的調用程序成功地調用完全受信任的程序集方法,可以請求適當的權限來確保部分受信任的調用程序不會損害系統,然后斷言特定的權限以執行高特權操作。
在進行特權操作之前,應該調用完全受信任的程序集中的 Assert,并隨后調用 RevertAssert以確保方法調用后面的代碼不會因疏忽而成功,因為 Assert 仍然在發揮作用。您應該將此代碼放在私有函數中,這樣,Assert 就會自動在方法返回之后從堆棧中刪除(使用 RevertAssert 調用)。使此方法私有非常重要,這樣攻擊者就無法使用外部代碼來調用該方法。
考慮下面的示例。
Private void PrivilegedOperation()
{
// Demand for permission.
new FileIOPermission( PermissionState.Unrestricted ).Demand();
// Assert to allow caller with insufficient permissions.
new FileIOPermission( PermissionState.Unrestricted ).Assert();
// Perform your privileged operation.
}
在默認的情況下,完全受信任的程序集并不允許從部分受信任的應用程序或程序集中進行調用;這樣的調用會引起安全異常。為了避免這些異常,可以將 AllowPartiallyTrustedCallersAttribute (APTCA) 添加到 Visual Studio .NET 生成的 AssemblyInfo.cs 文件中,如下所示。
[assembly: AllowPartiallyTrustedCallersAttribute()]
注應該對使用 APTCA 的代碼進行評審,以確保它不會被任何部分受信任的惡意代碼所利用。有關詳細信息,請參閱 Improving Web Application Security: Threats and Countermeasures 的“Chapter 8 — Code Access Security in Practice”中的“APTCA”,地址在 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/THCMCh08.asp。
考慮使用強名稱的程序集
使用強名稱可以提高程序集的安全性。您應該考慮使用強名稱來簽署您的應用程序中的所有程序集,并且修改安全策略以信任此強名稱。您可以使用 Sn.exe 工具簽署帶有強名稱密鑰對的程序集。要手動更改安全策略,可以使用 .NET Framework Configuration MMC 管理單元或 Caspol.exe,一個命令行工具(位于 %SystemRoot%\Microsoft.NET\Framework\< version>\CasPol.exe)。
使用私鑰簽署程序集的過程應該進行如下考慮:
? |
將延遲簽署用于開發。編譯代碼的編譯過程可以使用延遲簽署,從而使用強名稱密鑰對的公共部分而不是私鑰。要使用延遲簽署,開發人員可以將下面的屬性添加到項目的 Assembly.cs 文件中。 [assembly:AssemblyKeyFile("publickey.snk")]
[assembly:AssemblyDelaySign(true)]
|
? |
保護生成的私鑰。下面的命令行展示了強名稱工具 (Sn.exe) 的使用,該工具是 .NET Framework SDK 附帶的,用于生成直接針對可移動存儲設備的密鑰對 (Keypair.snk)。(在本例中,使用的 F 驅動器是 USB 驅動器) sn -k f:\keypair.snk
sn -p f:\keypair.snk f:\publickey.snk
公鑰 (Publickey.snk) 用于開發人員進行延遲簽署。該密鑰對存儲在具有高度受限的訪問權限的安全位置。 |
? |
禁用對測試的驗證。要測試已經延遲簽署的程序集,可以使用 Sn.exe 在測試計算機上注冊它。表 5.1 列出了常用的命令行變體。
表 5.1 常用的命令行變體
sn -Vr assembly.dll |
禁用對特定程序集的驗證。 |
sn -Vr *,publickeytoken |
禁用對具有特定公鑰的所有程序集的驗證。星號 (*) 通過對應于為忽略驗證提供的公鑰令牌的密鑰來注冊所有延遲簽署的程序集。 |
|
? |
通過供發布的私鑰進行簽署。要完成簽署過程,可以使用下面的命令通過私鑰進行簽署。 sn -r assembly.dll f:\keypair.snk
在簽署程序集以供在組織中使用之前,指定的組成員應該對其進行測試和評審。 |
有關延遲簽署和本節中解釋的過程的詳細信息,請參閱 Improving Web Application Security: Threats and Countermeasures 中的下列資源:
同時參閱下列資源:
避免給受限區域賦予完全的信任
作為解決部分受信任的應用程序的安全問題的一個快速替代解決方案,您可能非常想給受限區域(例如 Internet 或本地 Intranet 區域)賦予完全的信任。這樣做會允許任何應用程序在沒有進行代碼訪問安全檢查的情況下就運行在本地系統上,如果應用程序來自惡意的來源,這就成為一個問題。然而,如果在設計階段考慮部署方案,就不必開放安全性來允許應用程序運行。
設計完全受信任的應用程序
因為部分受信任的應用程序可能對系統資源具有非常少的權限,所以為了正確地運行,應用程序可能需要比默認分配給它的權限更多的權限。需要能夠執行諸如啟動 Office 應用程序或 Microsoft Internet Explorer、調用舊式 COM 組件、寫入文件系統這樣的任務的應用程序必須具有顯式啟用這些操作的權限才能運行。
將應用程序指定為完全受信任的應用程序,這樣就可以給它分配所有可能的權限,這對我們是很有吸引力的。然而,更安全的做法是,在設計和部署應用程序時,只要求應用程序正確運行所需的最小權限。如果您確實需要將應用程序作為完全受信任的應用程序來運行,就必須考慮使用下列指導原則:
? |
確定程序集需要訪問的資源的類型,評估在程序集受到損害時可能發生的潛在威脅。 |
? |
確定目標環境的信任級別,因為代碼訪問安全策略可能對程序集的功能進行限制。 |
? |
使用只用于構成程序集公共接口的一部分的類和成員的公共訪問修飾符來減少攻擊面。只要有可能,就使用 private 或 protected 訪問修飾符來限制對所有其他類和成員的訪問。 |
? |
使用 sealed 關鍵字來防止對沒有設計為基類的類的繼承,如下面的代碼所示。 public sealed class NobodyDerivesFromMe
{...}
|
? |
只要有可能,就使用聲明性類級別或方法級別屬性來限制對指定的 Windows 組的成員的訪問,如下面的代碼所示。 [PrincipalPermission(SecurityAction.Demand,Role=@"DomainName\WindowsGroup")]
public sealed class Orders()
{...}
|
? |
為延遲簽署、測試、安全評審和保護私鑰建立安全構建過程。 |
小結
智能客戶端應用程序在本質上是分布式的,因此,為了有效地管理它們的安全性,需要考慮服務器上的安全、客戶端上的安全、以及兩者之間的網絡連接的安全。具體的智能客戶端考慮事項包括設計安全身份驗證、授權、數據驗證和保護敏感數據。您還應該研究如何使用代碼訪問安全機制來管理代碼級而不是用戶級的安全性。
轉到原英文頁面