在.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中配置了网站的信任级别:< 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 ="" />
< 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文件,修改后的文件如下所示:粗体部分为修改点
<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数据库,不能访问其他数据库,用户名密码等可以自由输入,也就是在代码中只能:
如果连接其他数据库就会报错:
异常详细信息: System.Security.SecurityException: 请求“System.Data.SqlClient.SqlClientPermission, System.Data, Version = 2.0 . 0.0 , Culture = neutral, PublicKeyToken = b77a5c561934e089”类型的权限已失败。
这样就从源头上限制住了数据库的连接操作。
当然如果希望可以连接任意数据库,可以修改为如下配置:
实现和使用一个最简版的自定义权限
自定义一个代码访问权限需要从CodeAccessPermission继承,并且要实现IUnrestrictedPermission接口,主要需实现的方法有:
Copy 创建当前权限对象的副本。
Intersect 返回当前类与传递的类所允许权限的交集。
IsSubsetOf 如果传递的权限包括当前权限允许的一切操作,则 IsSubsetOf 返回 true。
FromXml 对您的自定义权限的 XML 表示形式进行解码。
ToXml 对您的自定义权限的 XML 表示形式进行编码。
Union 创建一个权限,该权限是当前权限与指定权限的并集。
2 using System.Text;
3 using System.Security;
4 using System.Security.Permissions;
5
6 namespace 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文件中添加配置:
要不然会报粗:该程序集不支持部分受信任的调用方
5、在web_customtrust.config文件中加入配置
a、在SecurityClasses节点加入:
b、在Name="ASP.Net"的PermissionSet节点加入:
配置好之后就可以在代码中应有此安全策略了
private void Test()
{
CustomPermission Perm = new CustomPermission();
Perm.Demand();
.......
.......
}
这个是应用安全策略的一种方式,另外一种方式是使用声明式安全性,需要再定义一个属性类来支持,这里就不再详述了。
小结:总之代码访问安全性可以有效的限制代码的使用权限,本文的例子只是用来演示使用方法,实际应用会比这个复杂的多。比如在Sharepoint中可能允许客户上传自己的dll代码,客户的代码有可能调用系统资源和我们提供的共通代码,那么我们就可以通过代码访问安全性来限制客户代码可以访问哪些资源,可以调用哪些共通dll等。