托管代碼的優點 開發 .NET Framework 應用程序提供了一些直接的安全性優點,但是還有許多問題需要考慮。這些問題在本指南第三部分的幾個“構建……”單元中進行了討論。 .NET Framework 程序集是用托管代碼生成的。各種語言的編譯器(例如 Microsoft Visual C#_ 開發工具和 Microsoft Visual Basic疇.NET 開發系統)都輸出 Microsoft 中間語言 (MSIL) 指令,它們包含在標准 Microsoft Windows 可移植的執行文件 (PE) .dll 或 .exe 文件中。當加載程序集並且調用一個方法時,方法的 MSIL 代碼將被實時 (JIT) 編譯器編譯為本機的機器指令,隨後再執行這些機器指令。不會調用到的方法將不進行 JIT 編譯。 中間語言與公共語言運行庫所提供的運行庫環境配合使用,為程序集開發人員提供了許多直接的安全性優點。 文件格式與元數據驗證。 公共語言運行庫能夠驗證 PE 文件格式的有效性,以及地址不指向 PE 文件之外。這有助於提供程序集獨立。公共語言運行庫還能驗證程序集中所包含的元數據的完整性。 代碼驗證。MSIL 代碼在進行 JIT 編譯時會驗證類型安全性。從安全角度來看,這是一個很大的優點,因為驗證過程可以防止不良的指針操作,可以驗證類型轉換的合法性,檢查數組邊界,等等。這實際上消除了托管代碼中出現緩沖溢出漏洞的可能,雖然仍舊需要仔細檢查調用非托管應用程序編程接口 (API) 的代碼是否存在緩沖溢出的可能。 完整性檢查。強名稱程序集的完整性需要使用數字簽名來驗證,以確保程序集從生成和簽名之後沒有發生任何改變。這意味著攻擊者無法通過直接操作 MSIL 指令來改變代碼。 代碼訪問安全性。公共語言運行庫提供的虛擬執行環境還允許在運行時執行更多安全檢查。說得具體一些,就是代碼訪問安全性能夠根據調用代碼的標識進行各種運行時安全決策。 用戶安全性與代碼安全性 用戶安全性和代碼安全性是 .NET Framework 應用程序中兩種相輔相成的安全形式。用戶安全性回答的是“用戶是誰,用戶能進行什麼操作?”,而代碼安全性回答的是這樣的問題:“代碼從何而來,誰編寫了代碼,代碼能執行什麼操作?” 代碼安全性涉及的是授權對應用程序(而非用戶)系統級資源(包括文件系統、注冊表、網絡、目錄服務和數據庫)的訪問。在此情況下,無論最終用戶是誰,或者哪個用戶帳戶運行代碼都是無關緊要的,而什麼代碼以及是否允許進行這樣的操作則非常重要。 .NET Framework 用戶安全性的實現稱為基於角色的安全性。 代碼安全性的實現稱為代碼訪問安全性。 基於角色的安全性 .NET Framework 基於角色的安全性允許 Web 應用程序根據與應用程序交互的用戶的標識或者角色成員身份進行安全決策。如果應用程序使用 Windows 身份驗證,,則角色就是 Windows 組。如果應用程序使用其他形式的身份驗證,則角色就是由應用程序定義的,用戶和角色的詳細信息通常維持在 SQL Server 或者基於 Active Directory 的用戶存儲中。 已經進行身份驗證的用戶的身份及其相關的角色成員身份是 Web 應用程序通過 Principal 對象獲得的,後者附加在當前 Web 請求中。 圖 1 顯示了 Web 應用程序中如何使用用戶安全性來限制用戶訪問 Web 頁、業務邏輯、操作和數據訪問的典型邏輯視圖。 圖 1. 基於(用戶)角色的安全性的邏輯視圖 代碼訪問安全 代碼訪問安全可以在代碼試圖訪問安全的資源(例如,文件系統、注冊表、網絡等等)時,或者試圖執行其他特權操作(例如,調用非托管代碼或者使用反射)時,對其進行授權。 代碼訪問安全性是一種重要的附加防范機制,可以用來提供對一段代碼的約束。管理員可以配置代碼訪問安全策略以限制代碼能夠訪問的資源類型以及能夠執行的其他特權操作。從 Web 應用程序的角度來看,這意味著如果在某個危及安全的攻擊過程中,攻擊者控制了 Web 應用程序進程或者插入了在進程中運行的代碼,則代碼訪問安全性提供的這些附加約束就能夠限制可能帶來的損害。 圖 2 顯示了在 Web 應用程序中如何使用代碼訪問安全性來限制應用程序訪問系統資源、其他應用程序擁有的資源和特權操作(如調用非托管代碼)的邏輯視圖。 圖 2. 基於代碼的安全性的邏輯視圖 代碼的身份驗證(標識)是根據代碼的證據(例如,它的強名稱、發行者,或者安裝目錄)進行的。而授權是根據安全策略授予代碼的代碼訪問權限進行的。有關 .NET Framework 代碼訪問安全性的詳細信息,請參閱“代碼訪問安全性實踐”單元。 .NET Framework 基於角色的安全性 .NET Framework 基於角色的安全性是在應用程序中用來授權用戶操作的關鍵技術。角色經常用來強制執行一些業務規則。例如,財務應用程序可能只允許經理執行超過某個特定金額上限的轉賬操作。 基於角色的安全性由以下要素組成: 用戶和標識 PrincipalPermission 對象 基於角色的安全性檢查 URL授權 用戶和標識 基於角色的安全性是通過 Principal 和 Identity 對象實現的。已進行身份驗證的調用方的標識和角色成員身份是通過 Principal 對象公開的,該對象附加在當前 Web 請求中。可以使用 HttpContext.Current.User 屬性檢索該對象。如果應用程序不需要對調用方進行身份驗證,例如,用戶正在浏覽站點中可以公開訪問的部分,則 Principal 對象代表的是匿名 Internet 用戶。 Principal 對象有很多類型,准確的類型取決於應用程序所使用的身份驗證機制。但是,所有 Principal 對象都要實現 System.Security.Principal.IPrincipal 接口,它們都維持著一個角色列表,用戶是其中的一個成員。
Principal 對象還包含 Identity 對象,後者包括用戶名以及指示身份驗證類型和用戶是否已經進行身份驗證的標志。這樣就能夠區分已進行身份驗證和匿名的用戶。Identity 對象也有許多不同類型,取決於身份驗證的類型,雖然所有標識對象都要實現 System.Security.Principal.IIdentity 接口。 以下表格給出了可能的身份驗證類型以及 ASP.NET Web 應用程序使用的 Principal 和 Identity 對象的各種不同類型。 PrincipalPermission 對象 PrincipalPermission 對象表示當前用戶執行代碼所必需的標識和角色。PrincipalPermission 對象可以在代碼中聲明性或命令性地使用。 聲明性安全 可以通過在類或者方法定義中添加 PrincipalPermissionAttribute,准確地控制允許哪些用戶訪問這些類或者方法。類級別的屬性自動應用於所有類成員,除非它已被成員級屬性所重寫。PrincipalPermissionAttribute 類型是在 System.Security.Permissions 命名空間中定義的。 注 還可以使用 PrincipalPermissionAttribute 限制對結構和其他成員類型(如屬性和委托)的訪問。 下面的示例說明了如何將對某個特定的類的訪問權限制在 Managers 組的成員中。請注意該示例假設使用 Windows 身份驗證,其中角色名的格式是 MachineName\RoleName 或者 DomainName\RoleName。對於其他的身份驗證類型,角色名的格式是特定於應用程序的,並取決於用戶存儲中保存的角色名字符串。 [PrincipalPermissionAttribute(SecurityAction.Demand, Role=@"DOMAINNAME\Managers")] public sealed class OnlyManagersCanCallMe { } 注 屬性類型名中最後的 Attribute 可以省略。這樣屬性類型名就與其相關的權限類型名相同了,在此例中為 PrincipalPermission。它們是截然不同的(但邏輯上是相關的)類型。 下面的示例說明了如何限制對一個類的特定方法的訪問。在此示例中,訪問僅限於本地管理員組的成員,這是通過特殊的 "BUILTIN\Administrators" 標識符來標識的。 [PrincipalPermissionAttribute(SecurityAction.Demand, Role=@"BUILTIN\Administrators")] public void SomeMethod() { } 其他內置的 Windows 組名可以通過在組名前加上"BUILTIN\"(例如,"BUILTIN\Users" 和 "BUILTIN\Power Users" )使用。 命令性安全 如果方法級安全性對於您的安全需求來說還不夠細致,可以在代碼中執行命令性安全檢查,方法是使用 System.Security.Permissions.PrincipalPermission 對象。 以下示例說明了使用 PrincipalPermission 對象的命令性安全語法。 PrincipalPermission permCheck = new PrincipalPermission( null, @"DomainName\WindowsGroup"); permCheck.Demand(); 為了避免使用局部變量,以上代碼還可以寫成: (new PrincipalPermission(null, @"DomainName\WindowsGroup")).Demand(); 以上代碼用一個空的用戶名和指定的角色名創建了一個 PrincipalPermission 對象,然後調用了 Demand 方法。這將導致公共語言運行庫查詢附加於當前線程的當前 Principal 對象,檢查相關聯的標識是否為指定角色的成員。因為本例中使用了 Windows 身份驗證,所以角色的檢