在Windows作業系統中,一般我們比較常用的安全系統是以使用者或角色為主的安全性機制(role-base security, RBS), 透過對使用者或角色的管理,達到權限控管的目的,這也是大家比較熟悉的權限管理機制。
如果當我們使用一個外來的組件來存取磁碟,為了確保它內部無法執行任何的惡意行為,你希望限定這個組件只能存取特定目錄,像這樣子的狀況,就無法使用 RBS 達到規範,而必須透過 CAS 管控到程式碼身上才有辨法。
什麼是程式碼安全性 ( Code Access Security, CAS )
程式碼存取安全性(Code Access Security, CAS)是由.NET 之後才開始加入Windows,採用和 RBS 一致的模型,它是由 CLR 所管理,使用 evidence-based 的安全機制,控管應用程式的使用權限。 例如,當使用一個外來的組件時,為了確保它內部無法執行任何的惡意行為,我們可以使用 CAS 控管,限定它只可以進行讀取,無法進行寫入與刪除; 亦或者當執行一個組件必須到某個網站下載資料時,我們也可以限定它僅僅可以連結到這個指定的網址捉取資料,限定它無法連結至其他網址或發送Email等行為。 像這樣子的狀況,都無法使用 RBS 達到規範,而必須管控到更細節的程式碼身上才有辨法。 .NET所提供 CAS 機制,正是用來允許設計師,使用限定程式碼可執行的工作範圍,進而達到應用程式的安全性。
CAS 是利用 Secure Library 的概念,也就是以安全的基礎函式庫為基礎。 例如,當應用程式要去存取網路時,必須利用.NET Framework 所提供的 Socket、Web、TCP 等網路組件才能存取,而這些組件會要求對應的權限,如 DncPermission、WebPermission、SocketPermission 等等。 也就是說,不同的資源透過相對應的權限類別以控制組件執行的權限。 如果,設計者在呼叫該組件時降低了這些權限或是拒絕存取,則 Socket、Web、 TCP 等網路組件在要求權限時就會產生例外,藉此建立以程式碼為對象的安全機制。
CAS 是一個安全系統,它允許系統管理員或程式開發人員直接控制應用程式的授權 (authorization) 以達到安全性的需求。 它可以控管授權的項目,包含了幾乎所有 RBS 機制可以控管授權的項目例如:
- The file system
- The registry
- Printers
- The event logs
除此之外,CAS 也可以控管 RBS 無法控管的資源,例如:允許應用程式是否可以送出 Web Request,或者是否允許應用程式執行 DNS 查詢需求。 這些控管的目的都是因為有一些惡意程式會利用系統功能的呼叫,以侵入使用者隱私,所以最好還是要使用CAS來限制應用程式的存取權限。
CAS 只適用於 Managed 應用程式,無法管理 Unmanaged 應用程式,Unmanaged 應用程式須以作業系統的 RBS 機制另外控管。
若某個組件受 CAS 限制權限,那麼這個組件就被認定為部分信任組件(partially trusted)。 部分信任的組件,每次存取受保護的資源時,都會被要求 CAS 的檢查,以便套用程式碼存取安全性。
PS.
部分信任的應用程式大都是指從外部來源 (例如網際網路) 載入的應用程序,必須在沙箱中執行 (例如,在 IE 中)。 在桌面上或本機內部網路上安裝的應用程式,都會以完全信任來執行。 完全信任的應用程序不會受CAS影響,除非將它們標記為安全性透明,因為它們都是完全受信任的。
CAS的元素
System.Security.Policy 命名空間包含程式碼群組、成員資格條件和辨識項。 這三種類別都是用來建立給 CLR 套用的安全性原則。 Evidence classes are the input to security policy and membership conditions are the switches(參數); together these create policy statements and determine the granted permission set. Policy levels and code groups are the structure of the policy hierarchy.Code groups are the encapsulation of a rule and are arranged hierarchically in a policy level.
什麼是「辨識項」(Evidence)
任何安全系統都會有一個認證的機制,CAS 也不例外。 一般以人為對象的安全系統 (RBS),其認證機制通常會使用帳號密碼來辯識。 但是 CAS 的對象是應用程式,它則是利用辨識項 (Evidence) 來認證組件。 辨識項就像是程式碼的通行證一樣,CLR 會檢查這個通行證,以便決定是否讓程式碼存取資源。
簡單講,辨識項就是一組資訊,給 CLR 辨別用,以便決定該授與程式碼哪些權限。
例如常見的辨識項格式有:組件由哪個資料夾或網站執行、組件的簽名、發行者、強式名稱、hash。CLR 就是透過這資訊來認證組件。
所以說,辨識項並沒固定格式,它可能是任何事物,只要安全性原則可辨認的物件即可。
CLR 會在執行階段時,才經由組件的辨識項決定該組件屬於哪一個程式碼群組 (CodeGroup)。相對的,組件的執行權限,就是由程式碼群組所賦予的。
下表是 System.Security.Policy 命名空間裡常用的 Evidence 類別:
- ApplicationDirectory :以應用程式目錄做為原則評估的辨識項。
- GacInstalled :確認程式碼是否為全域組件快取 (GAC) 中的組件。
- Hash :以組件雜湊值 (Hash Value)做為原則評估的辨識項。
- Publisher :以組件發行者的數位簽章做為原則評估的辨識項。
- Site :以組件的來源網站做為原則評估的辨識項。例如:www.microsoft.com
- StrongName :程式碼組件的強式名稱做為原則評估的辨識項。
- URL :程式碼組件的來源 URL 做為原則評估的辨識項。例如:http://www.microsoft.com/news/index.htm
- Zone :程式碼組件的安全性區域 (Security Zone) 做為原則評估的辨識項。
其實辨識項它也就是程式碼群組的成員資格條件,請參考下圖:
如何建立辨識項:
Evidence evd = new Evidence(); // Create a Url name. Url url = new Url("http://www.microsoft.com"); evd.AddHostEvidence(url); evd.AddAssemblyEvidence(url); // Create a strong name. StrongName mSN = new StrongName(new StrongNamePublicKeyBlob(null), "SN01", new Version("0.0.0.0")); evd.AddHostEvidence(mSN); evd.AddAssemblyEvidence(mSN);
列舉組件的辨識項:
Assembly[] asms = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly asm in asms) { Console.WriteLine(asm.ToString()); Evidence evidence = asm.Evidence; object[] evidenceArray = new object[evidence.Count]; evidence.CopyTo(evidenceArray, 0); foreach (object obj in evidenceArray) { Console.WriteLine(obj.ToString()); } }
下表是其中一個組件的 Evidence ,
<System.Security.Policy.GacInstalled version="1"/> <System.Security.Policy.Hash version="2"> <hash algorithm="SHA1" value="F2B76C91B22A271D84DAD801191B74D3A85BED15"/> <hash algorithm="SHA256" value="D5EEABC20682B71014E55EA35FA0A8FFBFFCAEBA8DBE2A8B52C00C4B146286B2"/> <hash algorithm="MD5" value="EC4F3A5008A3F8A45973784CB3B97540"/> </System.Security.Policy.Hash> <StrongName version="1" Key="00000000000000000400000000000000" Name="mscorlib" Version="4.0.0.0"/> <System.Security.Policy.Url version="1"> <Url>file:///C:/Windows/Microsoft.Net/assembly/GAC_32/mscorlib/v4.0_4.0.0.0__b77a5c561934e089/mscorlib.dll</Url> </System.Security.Policy.Url> <System.Security.Policy.Zone version="1"> <Zone>MyComputer</Zone> </System.Security.Policy.Zone>
Assembly testAssembly = Assembly.LoadFile(@"D:\myBlog\TestProj\TestForAppDomain\TestForAppDomain\bin\Debug\TestForAppDomain.exe"); Evidence evidence = testAssembly.Evidence; object[] evidenceArray = new object[evidence.Count]; evidence.CopyTo(evidenceArray, 0); foreach (object obj in evidenceArray) { Console.WriteLine(obj.ToString()); }
<System.Security.Policy.Hash version="2"> <hash algorithm="SHA1" value="1720F486CCF9801D95AF42F9FFE8D0E36DB107C6"/> <hash algorithm="SHA256" value="547D24E42BCCE76F3C0F26F77FE0BAFCCB0119A195DCB4A3D3BD4916475FD07F"/> <hash algorithm="MD5" value="19429C66435A0228802DD88BBC263DDB"/> </System.Security.Policy.Hash> <System.Security.Policy.Url version="1"> <Url>file:///D:/myBlog/TestProj/TestForAppDomain/TestForAppDomain/bin/Debug/TestForAppDomain.exe</Url> </System.Security.Policy.Url> <System.Security.Policy.Zone version="1"> <Zone>MyComputer</Zone> </System.Security.Policy.Zone>
辨識項可分成二類:
Evidence 類別有二個用來儲存辨識項集合:主辨識項和組件辨識項。- 主辨識項 (Host Evidence):
執行階段才由主應用程式提供的辨識項,這類辨識項通常是程式碼的來源或組件上的數位簽章。
來源辨識項:例如 Url、Site、Zone ,是形容某一組件來源的辨識項。
簽章辨識項:例如 Strong Name、Publisher 辨識項。
來源辨識項的資訊因為位於主應用程式中,所以由主應用程式提供。
Strong Name 和 Publisher 這二種簽章的識別雖然內建在組件中,但是它必須先由主應用程式驗證後再傳遞至原則。 - 組件辨識項 (Assembly Evidence):
組件辨識項是組件它自己的一部分,它只能在組件產生時加入。例如 Hash 、 Publisher 或 StrongName。
不過安全系統的預設原則會忽略組件提供的辨識項。
object[] hostEvidence = { new Zone(SecurityZone.Internet), new Url("http://www.codeplex.com/hostevidence") }; object[] assemblyEvidence = { new Zone(SecurityZone.MyComputer), new GacInstalled() }; Evidence evidence = new Evidence(hostEvidence, assemblyEvidence); evidence.AddHost(new Site("www.codeplex")); evidence.AddAssembly(new Url("http://www.codeplex.com/assemblyevidence"));
什麼是「使用權限」(Permission)
Permission 就是 CAS 機制中的一個存取控制項,在.NET Framework 組態工具裡,共有 19 個預設的使用權限可供配置。 例如:「檔案對話方塊」這個權限,可以用來決定一個組件是否可以開啟對話方塊或儲存對話方塊。 每個權限都對應到 System.Security.Permissions 命名空間裡的二個成員:命令式使用及宣告式使用 ( imperative use & declarative use )。
下圖中,在組態管理工具中,共有19個預設的使用權限可用,在 System.Security.Permissions 裡,也都有相對應的類別。
權限集合 and 使用權限
[WebPermission(SecurityAction.Deny, ConnectPattern = @"http://www\.microsoft\.com/")] private void button7_Click(object sender, EventArgs e) { HttpWebRequest myWebRequest = (HttpWebRequest)WebRequest.Create("http://www.microsoft.com"); Console.WriteLine("由於權限設定不允許連線這個網站,所以程式碼不會到這一行"); }
什麼是「權限集合」(Permission Set)
權限集合是 CAS 機制中的存取控制清單 (Access Control List, ACL),在 .NET Framework 組態工具裡,有七個預設的權限集合,也可以自行再定義擴充。而毎個權限集則可以設定數種使用權限。
.NET Framework 預設的七個權限集合,如下表所示,每個權限集合都包含相關的使用權限:
- FullTrust:完全信任
- Execution:執行
- LocalIntranet:內部區域網路
- Internet:網際網路
- SkipVerification:略過驗證
- Everything:
- Nothing:
底下程式碼示範 PermissionSet 的使用
//Open a new PermissionSet. PermissionSet myPermSet = new PermissionSet( PermissionState.None); // Add a permission to the permission set. myPermSet.AddPermission(new FileIOPermission (FileIOPermissionAccess.Read, @"C:\Windows")); myPermSet.AddPermission(new FileIOPermission( FileIOPermissionAccess.Write, @"C:\Inetpub")); myPermSet.AddPermission(new RegistryPermission( RegistryPermissionAccess.Write, @"HKEY_LOCAL_MACHINE\Software")); myPermSet.Demand();
列舉 PermissionSet
底下程式碼會取得系統中,符合目前組件的權限集合,而不是列舉所有的權限集合,
private void ListMachinePermissionSets() { IEnumerator policyEnumerator = SecurityManager.PolicyHierarchy(); while (policyEnumerator.MoveNext()) { PolicyLevel currentLevel = (PolicyLevel)policyEnumerator.Current; if (currentLevel.Label == "Machine") { IList namedPermissions = currentLevel.NamedPermissionSets; IEnumerator namedPermission = namedPermissions.GetEnumerator(); while (namedPermission.MoveNext()) { Console.WriteLine("\t" + ((NamedPermissionSet)namedPermission.Current).Name); } } } }
在沙箱中,使用自訂的 PermissionSet
private void button25_Click(object sender, EventArgs e) { string pluginFolder = @"D:\myBlog\TestProj\TestForAppDomain\TestForAppDomain\bin\Debug"; string plugInPath = Path.Combine(pluginFolder, "TestForAppDomain.exe"); AppDomainSetup setup = new AppDomainSetup(); setup.ApplicationBase = pluginFolder; Evidence hostEvidence = new Evidence(); hostEvidence.AddHost(new Url(pluginFolder)); PermissionSet pset = GetNamedPermissionSet("myPermissionSet"); pset.AddPermission(new FileIOPermission(FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read, pluginFolder)); AppDomain sandbox = AppDomain.CreateDomain("sbox", null, setup, pset); sandbox.ExecuteAssembly(plugInPath); AppDomain.Unload(sandbox); } private static PermissionSet GetNamedPermissionSet(string name) { IEnumerator policyEnumerator = SecurityManager.PolicyHierarchy(); PolicyLevel level = PolicyLevel.CreateAppDomainLevel(); while (policyEnumerator.MoveNext()) { PolicyLevel currentLevel = (PolicyLevel)policyEnumerator.Current; if (currentLevel.Label == "Machine") { NamedPermissionSet copy = currentLevel.GetNamedPermissionSet(name); return (PermissionSet)copy; } } return null; }
什麼是「程式碼群組」(Code Groups)
「程式碼群組」的作用是將組件與權限集合產生關聯。
程式碼群組的屬性中有二項資訊:
- 成員資格條件 (membership condition):設定 Evidence 類別的詳細資訊。
- 使用權限集合 (permission set):設定程式碼群組的權限。
前面說過,「程式碼群組」的作用是將組件與權限集合產生關聯。 這裡所指的組件,指的是符合特定條件的所有組件。 例如:若在程式碼群組中,將組件的條件類型設定為 ApplicationDirectory ,表示與應用程式同一目錄或子目錄下的所有組件。
例:下圖程式碼群組的設定指出,只要符合 URL 類型的成員,且符合指定的 URL 格式,都將套用到「唯讀組件集合」這個自訂的權限集合。
預設的程式碼群組共5個:
- My_Computer_Zone:本機電腦(完全信任)。
- LocalIntranet_Zone:內部網路。
- Tursted_Zone:信任的網站。
- Internet_Zone:網際網路。
- Restricted_Zone:。限制的網站(Nothing)
例如下圖中, Internet_Zone 這個程式碼群組,它使用區域這個類型的 Evidence 。也就是這個程式碼群組以區域 (Zone) 做為組件辨識的類型。 而且更明確的指明是網際網路(Internet)這個區域類型。(區域共分6種來源地區,如 Internet 、 Intranet 、 MyComputer)
同一程式碼群組的組件具有相同的權限
這機制跟使用者群組有點像,我們設定好使用者群組的權限,再把使用者加入,那麼這些使用者就具有相同權限。 程式碼群組也是如此,但是它沒有手動將組件加入程式碼群組的步驟。 因為,組件是在 CLR 執行階段時,經由組件的辨識項判斷,才知道此時組件屬於哪個程式碼群組。
例如,如果群組的成員資格條件為 "Code from the www.microsoft.com Web site",執行階段便會檢查辨識項,判斷程式碼的來源是否為 www.microsoft.com。
若要指定程式碼群組的權限,可以切換到使用權限集合這個頁簽,如下圖所示。 不過此時你會發現,它無法單獨指定一個權限,只能指定權限集合。 是的,沒錯,程式碼群組只能指派 Permission Set 以獲得 Permission,不能直接指派 Permission,而且只能套用一個 Permission Set 。
雖然一個 CodeGroup 只能指派一個[成員資料條件]及單一的[權限集合],但是,就像一個 User 可以同時隸屬於不同的 User Group,一個組件也可以同時是多個 CodeGroup 的成員。 具有多個 CodeGroup 的組件,將會取得所有這些 CodeGroup 的權限(union of the permission sets)。 另外,一個 CodeGroup 也可以巢狀到另一個 CodeGroup,這樣子,一個 CodeGoup 就可能同時符合多個Evidence Type。
使用程式碼建立程式碼群組
下面程式碼示範如何使用程式建立 CodeGroup,要建立 code-group 必須決定要列事項:
- code-group 的層級
- 使用什麼 evidence
- 授與 code-group 什麼權限集合
protected CodeGroup(IMembershipCondition membershipCondition, PolicyStatement policy);
CodeGroup machineCodeGroupRoot = null; //To access a security level PolicyLevel policyMachineLevel = PolicyLevel.CreateAppDomainLevel(); machineCodeGroupRoot = policyMachineLevel.RootCodeGroup; //To provide evidence value Evidence evidence = new Evidence(); evidence.AddHost(new Site("www.microsoft.com")); //Register a code-group byte[] key = Assembly.GetCallingAssembly().GetName().GetPublicKey(); StrongNamePublicKeyBlob cdeGroupKey = new StrongNamePublicKeyBlob(key); PermissionSet ps = new PermissionSet(PermissionState.None); ps.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution)); UnionCodeGroup codegroup = new UnionCodeGroup( new StrongNameMembershipCondition(cdeGroupKey, null, null), new PolicyStatement(ps,PolicyStatementAttribute.Nothing)); codegroup.Name = "MyGroup"; machineCodeGroupRoot.AddChild(codegroup); SecurityManager.SavePolicyLevel(policyMachineLevel); //at last save the policy
下表是一些與程式碼群組相關的類別
- CodeGroup :
Represents the abstract base class from which all implementations of code groups must derive. - NamedPermissionSet :
Defines a permission set that has a name and description associated with it. - PolicyStatement :
Represents the statement of a CodeGroup describing the permissions and other information that apply to code with a particular set of evidence. - FirstMatchCodeGroup :
Allows security policy to be defined by the union of the policy statement of a code group and that of the first child code group that matches. - UnionCodeGroup :
Represents a code group whose policy statement is the union of the current code group's policy statement and the policy statement of all its matching child code groups.
安全性原則 ( Security Policy )
什麼是安全性原則
- 安全性原則由一些[程式碼群組]及[權限集合]邏輯性地組成。
- CLR 依據安全性原則授與程式碼的使用權限。
- 系統管理員可以彈性地以不同層級配置安全性原則。預設的四種原則層級:企業、本機電腦、使用者、應用程式。
- 預設的層級中,企業和使用者都是完任信任所有的程式碼,只有本機電腦有對CAS使用權限做限制。
每個安全性原則會定義幾個程式碼群組,並將每個程式碼群組與一組使用權限相關聯。
安全性系統會使用 Evidence 來判斷組件所屬的程式碼群組。在考慮完所有的辨識項 (Evidence) 之後,組件會與一個或多個程式碼群組產生關聯,而授與給此組件的使用權限,包括所有與相符程碼群組關聯的使用權限。
雖然預設安全性原則適用於大部分的情況,但是系統管理員仍可依據組織的特定需求來修改或自訂安全性原則。 Runtime 會根據安全性原則,將使用權限同時授與組件 (Assembly) 和應用程式定義域 (AppDomain)。
CAS 和作業系統安全性如何配合
CAS和作業系統安全性是共存的,當要決定某個組件是否有權限執行某個動作時,程式碼存取安全性和作業系統安全性都會同時被評估,然後取兩者之間最嚴格的權限集。 例如,某個組件由 CAS 取得寫入 C:\ 的權限,但是執行這個組件的使用者並沒有這個寫入 C:\ 的權限,那麼這個組件就沒有權限寫入這個資料夾。
如何設定執行階段安全性原則
要限制程式的安全性原則,可以在程式碼中透過,宣告組件的屬性 (Attribute) 指定適當的權限。 也可以由 .NET Framework 2.0 組態設定工具中設定。 另外,Caspot.exe 是微軟提供的一個以命令列的方式變更安全性原則的工具,也可以達到相同功能。
使用組件宣告方式,限制組件的執行權限
透過 FileIOPermission 類別,限制這個函式只能讀取 C:\temp 目錄中的檔案
[assembly: FileIOPermission(SecurityAction.RequestOptional, Read = @"C:\\temp")] namespace ClassLibrary1 { public class Class2 { public static string Open(string sFileName) { StreamReader reader = new StreamReader(sFileName); string Content = reader.ReadToEnd(); reader.Close(); return Content; } public static void Save(string sFileName, string Content) { StreamWriter writer = new StreamWriter(sFileName); writer.Write(Content); writer.Close(); } } }
private void button4_Click(object sender, EventArgs e) { string FileName = txtFileName.Text; //執行類別庫的讀取功能 ClassLibrary1.Class2.Open(FileName); //這行指令可正常執行 //執行類別庫的寫入功能 //這行指令會產生 FileIOPermission 使用權限要求失敗。 ClassLibrary1.Class2.Save(FileName, "ABC" + Environment.NewLine + System.DateTime.Now.ToString()); }
使用組態設定工具,限制組件的執行權限
底下過程為使用組態設定工具,設定組件A只可以唯讀 C:\temp ,設定完成後:(本示範僅限.net framework 2.0的組件)
1.組件A可以讀取 C:\temp (含子目錄)
2.組件A不可以寫入 C:\temp
3.組件A也不可以讀寫其他目錄
(1)建立 Permission Set
Step1:在[使用者]原則底下的[使用權限集合],新增一個 Permission Set,名稱為:唯讀組件
Step2:授與執行組件的權限給這個集合。
Step3:授與檔案IO的權限給這個集合,並設定指定路徑唯讀。
Step4:完成。
(2)建立 Code Group
Step1:在[使用者]原則底下的[程式碼群組][All_Code],新增一個 Code Group,名稱為:唯讀群組
Step2:選擇條件類型
因為我們要設定的組件具有強式名稱,所以可以使用強式名稱這個類型來指定這個組件。
要不然,使用 URL 這個類型來指定也可以。
Step3:設定要套用的權限集合。
Step4:完成。
Step5:開啟設定好的CG,勾選下圖選項,以限制這個 CG 不繼承它的上層 All_Code 的完全信任權限。
使用 Caspol.exe 設定執行階段安全性原則
Caspol.exe是微軟提供的工具,稱為「程式碼存取安全性原則工具」。可透過命令列方式,執行安全性原則的操作。
Caspol -? 可查詢全部的參數及說明,詳情可參考使用程式碼存取安全性原則工具 (Caspol.exe) 設定安全性原則
另外可參考保哥的介紹:CasPol.exe 程式碼存取安全性原則工具幾個常用的指令
底下是幾個常見指令示範:
查詢組件符合的群組
caspol -resolvegroup "C:\temp\test.exe"
查詢組件擁有的權限
caspol -resolveperm "C:\temp\test.exe"
將組件設為「完全信任」
caspol -addfulltrust "C:\temp\test.exe"
將最近儲存的本機電腦原則(Machine)原則復原。
caspol -machine -recover
假設想要將 Intranet 上某個共享目錄設為完全信任,可以參考以下的做法:
CasPol.exe -m -pp off -ag 1.2 -url file://///server/share/* FullTrust
- -m :設定為本機電腦原則(Machine)
- -pp off :關掉訊息回應
- -ag 1.2 :加入一個 codegroup 到 1.2 這個群組下面。一般 1.2 指的就是 「LocalIntranet」群組。
- -url :必須使用一個 UrlMembershipCondition 當做成員資格條件。
- FullTrust :透用「完全信任」權限集合。
沒有留言:
張貼留言