在.Net Framework中提供了代碼訪問安全性(Code Access Security),它的主要作用就是限制代碼的使用權限。可以控制各種系統資源的訪問權限、可以要求代碼的調用方擁有特定的權限......。比如我們可以控制自己的dll只能在什麼條件下由什麼人調用,特別是在Asp.net中可以限制不同代碼的安全權限,從源頭限制住網絡上的攻擊等。
本文的主要內容如下:
1、在Asp.Net中使用自定義的信任級別
2、配置Sqlconnection的代碼訪問權限
3、實現和使用一個最簡版的自定義權限
在Asp.Net中使用自定義的信任級別
Asp.Net默認在C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/CONFIG/web.config中配置了網站的信任級別:
<securityPolicy>
<trustLevel name="Full" policyFile="internal"/>
<trustLevel name="High" policyFile="web_hightrust.config"/>
<trustLevel name="Medium" policyFile="web_mediumtrust.config"/>
<trustLevel name="Low" policyFile="web_lowtrust.config"/>
<trustLevel name="Minimal" policyFile="web_minimaltrust.config"/>
</securityPolicy>
<trust level="Full" originUrl=""/>
默認爲Full,表示擁有最大的權限,當然風險也就最高,我們可以在自己的網站下的web.config中自定義信任級別:
<securityPolicy>
<trustLevel name="Custom" policyFile="E:/_NetProject/PermissionTrust/WebSite11/web_customtrust.config"/>
</securityPolicy>
<trust level="Custom" originUrl=""/>
這裏使用了自定義的配置文件,其實也就是複製C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/CONFIG/web_lowtrust.config文件,然後在此文件上進行適當修改就可以了(使用此配置默認是不允許連接數據庫的)
配置Sqlconnection的代碼訪問權限
配置的方法就是修改自定義的web_customtrust.config文件,修改後的文件如下所示:粗體部分爲修改點
web_customtrust.config
<configuration>
<mscorlib>
<security>
<policy>
<PolicyLevel version="1">
<SecurityClasses>
<SecurityClass Name="AllMembershipCondition" Description="System.Security.Policy.AllMembershipCondition, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<SecurityClass Name="AspNetHostingPermission" Description="System.Web.AspNetHostingPermission, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<SecurityClass Name="FileIOPermission" Description="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<SecurityClass Name="FirstMatchCodeGroup" Description="System.Security.Policy.FirstMatchCodeGroup, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<SecurityClass Name="IsolatedStorageFilePermission" Description="System.Security.Permissions.IsolatedStorageFilePermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<SecurityClass Name="NamedPermissionSet" Description="System.Security.NamedPermissionSet"/>
<SecurityClass Name="SecurityPermission" Description="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<SecurityClass Name="StrongNameMembershipCondition" Description="System.Security.Policy.StrongNameMembershipCondition, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<SecurityClass Name="UnionCodeGroup" Description="System.Security.Policy.UnionCodeGroup, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<SecurityClass Name="UrlMembershipCondition" Description="System.Security.Policy.UrlMembershipCondition, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<SecurityClass Name="ZoneMembershipCondition" Description="System.Security.Policy.ZoneMembershipCondition, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<SecurityClass Name="SqlClientPermission" Description="System.Data.SqlClient.SqlClientPermission, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
</SecurityClasses>
<NamedPermissionSets>
<PermissionSet
class="NamedPermissionSet"
version="1"
Unrestricted="true"
Name="FullTrust"
Description="Allows full access to all resources"
/>
<PermissionSet
class="NamedPermissionSet"
version="1"
Name="Nothing"
Description="Denies all resources, including the right to execute"
/>
<PermissionSet
class="NamedPermissionSet"
version="1"
Name="ASP.Net">
<IPermission
class="AspNetHostingPermission"
version="1"
Level="High"
/>
<IPermission
class="FileIOPermission"
version="1"
Read="$AppDir$"
PathDiscovery="$AppDir$"
/>
<IPermission
class="IsolatedStorageFilePermission"
version="1"
Allowed="AssemblyIsolationByUser"
UserQuota="1048576"
/>
<IPermission
class="SecurityPermission"
version="1"
Flags="Execution"
/>
<IPermission class="SqlClientPermission" version="1">
<add ConnectionString="data source=dbserver;initial catalog=db1"
KeyRestrictions="User ID=;Password=;Connection Reset="
KeyRestrictionBehavior="AllowOnly"/>
</IPermission>
</NamedPermissionSets>
<CodeGroup
class="FirstMatchCodeGroup"
version="1"
PermissionSetName="Nothing">
<IMembershipCondition
class="AllMembershipCondition"
version="1"
/>
<CodeGroup
class="UnionCodeGroup"
version="1"
PermissionSetName="ASP.Net">
<IMembershipCondition
class="UrlMembershipCondition"
version="1"
Url="$AppDirUrl$/*"
/>
</CodeGroup>
<CodeGroup
class="UnionCodeGroup"
version="1"
PermissionSetName="ASP.Net">
<IMembershipCondition
class="UrlMembershipCondition"
version="1"
Url="$CodeGen$/*"
/>
</CodeGroup>
<CodeGroup class="UnionCodeGroup" version="1" PermissionSetName="Nothing">
<IMembershipCondition
class="ZoneMembershipCondition"
version="1"
Zone="MyComputer" />
<CodeGroup
class="UnionCodeGroup"
version="1"
PermissionSetName="FullTrust"
Name="Microsoft_Strong_Name"
Description="This code group grants code signed with the Microsoft strong name full trust. ">
<IMembershipCondition
class="StrongNameMembershipCondition"
version="1"
PublicKeyBlob="002400000480000094000000060200000024000052534131000400000100010007D1FA57C4AED9F0A32E84AA0FAEFD0DE9E8FD6AEC8F87FB03766C834C99921EB23BE79AD9D5DCC1DD9AD236132102900B723CF980957FC4E177108FC607774F29E8320E92EA05ECE4E821C0A5EFE8F1645C4C0C93C1AB99285D622CAA652C1DFAD63D745D6F2DE5F17E5EAF0FC4963D261C8A12436518206DC093344D5AD293"
/>
</CodeGroup>
<CodeGroup
class="UnionCodeGroup"
version="1"
PermissionSetName="FullTrust"
Name="Ecma_Strong_Name"
Description="This code group grants code signed with the ECMA strong name full trust. ">
<IMembershipCondition
class="StrongNameMembershipCondition"
version="1"
PublicKeyBlob="00000000000000000400000000000000"
/>
</CodeGroup>
</CodeGroup>
</CodeGroup>
</PolicyLevel>
</policy>
</security>
</mscorlib>
</configuration>
加入以上的配置後限制使用SqlConnection時只能訪問dbserver上的db1數據庫,不能訪問其他數據庫,用戶名密碼等可以自由輸入,也就是在代碼中只能:
SqlConnection connection = new SqlConnection("data source=dbserver;User ID=gspring;Password=***;initial catalog=db1")
如果連接其他數據庫就會報錯:
說明: 應用程序試圖執行安全策略不允許的操作。要授予此應用程序所需的權限,請與系統管理員聯繫,或在配置文件中更改該應用程序的信任級別。
異常詳細信息: System.Security.SecurityException: 請求“System.Data.SqlClient.SqlClientPermission, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”類型的權限已失敗。
這樣就從源頭上限制住了數據庫的連接操作。
當然如果希望可以連接任意數據庫,可以修改爲如下配置:
<IPermission class="SqlClientPermission" version="1" Unrestricted="true"/>
實現和使用一個最簡版的自定義權限
自定義一個代碼訪問權限需要從CodeAccessPermission繼承,並且要實現IUnrestrictedPermission接口,主要需實現的方法有:
Copy 創建當前權限對象的副本。
Intersect 返回當前類與傳遞的類所允許權限的交集。
IsSubsetOf 如果傳遞的權限包括當前權限允許的一切操作,則 IsSubsetOf 返回 true。
FromXml 對您的自定義權限的 XML 表示形式進行解碼。
ToXml 對您的自定義權限的 XML 表示形式進行編碼。
Union 創建一個權限,該權限是當前權限與指定權限的並集。
1using System;
2using System.Text;
3using System.Security;
4using System.Security.Permissions;
5
6namespace MyPermission
7{
8 [Serializable]
9 public sealed class CustomPermission : CodeAccessPermission, IUnrestrictedPermission
10 {
11 private DateTime _expiredDate;
12
13 public DateTime ExpiredDate
14 {
15 get { return _expiredDate; }
16 set { _expiredDate = value; }
17 }
18
19 public CustomPermission()
20 {
21 }
22
23 //必須有這個方法,CAS系統會調用此方法的
24 public CustomPermission(PermissionState state)
25 {
26 }
27
28 public bool IsUnrestricted()
29 {
30 return false;
31 }
32
33 public override IPermission Copy()
34 {
35 CustomPermission copy = new CustomPermission();
36 copy.ExpiredDate = this.ExpiredDate;
37
38 return copy;
39 }
40
41 public override IPermission Intersect(IPermission target)
42 {
43 if (null == target)
44 {
45 return null;
46 }
47 else
48 {
49 return target;
50 }
51 }
52
53 private bool CheckDate(DateTime date)
54 {
55 if (System.DateTime.Now.CompareTo(date) < 0)
56 {
57 return true;
58 }
59 else
60 {
61 return false;
62 }
63 }
64
65 /**//// <summary>
66 /// 進行權限判斷
67 /// </summary>
68 /// <param name="target"></param>
69 /// <returns></returns>
70 public override bool IsSubsetOf(IPermission target)
71 {
72 if (null == target)
73 {
74 return false; //爲false時,指示條件不滿足,需要讀取config中配置來判斷
75 }
76 try
77 {
78 CustomPermission passedpermission = (CustomPermission)target;
79
80 return CheckDate(passedpermission.ExpiredDate);
81 }
82 catch (InvalidCastException)
83 {
84 throw new ArgumentException("Argument_WrongType", this.GetType().FullName);
85 }
86 }
87
88 public override void FromXml(SecurityElement PassedElement)
89 {
90 string element = PassedElement.Attribute("expireddate");
91
92 if (null != element)
93 {
94 this.ExpiredDate = Convert.ToDateTime(element);
95 }
96 }
97
98 public override SecurityElement ToXml()
99 {
100 SecurityElement element = new SecurityElement("IPermission");
101 Type type = this.GetType();
102 StringBuilder AssemblyName = new StringBuilder(type.Assembly.ToString());
103 AssemblyName.Replace('/"', '/'');
104 element.AddAttribute("class", type.FullName + ", " + AssemblyName);
105 element.AddAttribute("version", "1");
106 element.AddAttribute("expireddate", this.ExpiredDate.ToString());
107 return element;
108 }
109 }
110}
例子比較簡單,就是讀取配置中的過期時間進行判斷,需要特別說明的地方有:
1、public CustomPermission(PermissionState state)這個構造函數必須要有,CAS內部會調用此方法
2、將程序集添加到受信任的程序集列表中,因爲自定義權限將參與 .NET Framework 安全系統,所以它必須完全受信任。依次執行以下命令:
caspol -rf MyPermission.dll --從策略級別移除完全信任程序集
gacutil -i MyPermission.dll --註冊GAC
caspol -af MyPermission.dll --將完全信任程序集添加到策略級別
3、在將程序集加入GAC之後,會默認從GAC中讀取dll信息,不會讀取當前項目下新生成的dll,需要從GAC中把此DLL刪除後纔可以。ps:我當時在CustomPermission裏面加入一個新的方法,結果在自己的網站下一直找不到,把GAC中的信息刪除之後才能找到,不知道算不算VS2005的一個Bug
4、在MyPermission程序集的AssemblyInfo.cs文件中添加配置:
[assembly: AllowPartiallyTrustedCallers]
要不然會報粗:該程序集不支持部分受信任的調用方
5、在web_customtrust.config文件中加入配置
a、在SecurityClasses節點加入:
<SecurityClass Name="MyPermission" Description="MyPermission.CustomPermission, MyPermission, Version=1.0.0.0, Culture=neutral, PublicKeyToken=97b2744b86090fe0"/>
b、在Name="ASP.Net"的PermissionSet節點加入:
<IPermission class="MyPermission" version="1" expireddate="2008-07-22"/>
配置好之後就可以在代碼中應有此安全策略了
這個是應用安全策略的一種方式,另外一種方式是使用聲明式安全性,需要再定義一個屬性類來支持,這裏就不再詳述了。
小結:總之代碼訪問安全性可以有效的限制代碼的使用權限,本文的例子只是用來演示使用方法,實際應用會比這個複雜的多。比如在Sharepoint中可能允許客戶上傳自己的dll代碼,客戶的代碼有可能調用系統資源和我們提供的共通代碼,那麼我們就可以通過代碼訪問安全性來限制客戶代碼可以訪問哪些資源,可以調用哪些共通dll等。