应用程序集声明主要有三种——RequestMinimum, RequestOptional, and RequestRefuse,它们简述如下:
- RequestMinimum -- 程序集运行的绝对必要权限集。
- RequestOptional -- 程序集运行的可选权限集,拥有这些权限集会更好的执行,但非必要。
- RequestRefuse -- 程序集不应该被授予的权限集。
每一个程序集都可以使用上面三个中的任意一个或多个,最终程序集的权限集取决于已定义的所有特性(Attributes)定义的权限集的并集(union)。
RequestRefuse给人很直观的理解:任何权限集被加到程序集的拒绝列表,程序集就绝不会执行,也就是说,只要赋予了不该拥有的权限集,程序集就不会执行;而另外两个就有点晦涩了,尤其是它们所起的副作用(side effect)。为了演示RequestMinimum和RequestOptional,这里我打算用下面的代码,适当的赋予不同的程序集级别的安全声明来说明:
2 {
3 public static void Main()
4 {
5 Console.WriteLine("In Main");
6 if(HaveFileIO())
7 Console.WriteLine("Have full FileIO permission");
8 else
9 Console.WriteLine("Do not have full FileIO permission");
10 }
11
12 private static bool HaveFileIO()
13 {
14 try
15 {
16 new FileIOPermission(PermissionState.Unrestricted).Demand();
17 }
18 catch(SecurityException)
19 {
20 return false;
21 }
22
23 return true;
24 }
25}
RequestMinimum告诉CLR在没有赋予相应权限集时不能执行你的代码,所以如果我给程序集加上如下特性(attribute):
并且从Intranet zone执行程序集,是调用不成功的。当CLR扫描到程序集不能运行在没有提供无限制的文件读写权限集(unrestricted FileIO permission),并且发现本地网络权限(Intranet permission)绝不会赋予文件读写权限集时,加载器(loader)会拒绝加载程序集,同时,你会收到一个FileLoadException,它的内部异常是PolicyException,描述如下:
File name: 'ReqMin, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' --->
System.Security.Policy.PolicyException: Required permissions cannot be acquired.
at System.Security.SecurityManager.ResolvePolicy(Evidence evidence, PermissionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, PermissionSet& denied, Boolean checkExecutionPermission)
at System.Security.SecurityManager.ResolvePolicy(Evidence evidence, PermissionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, PermissionSet& denied, Int32& grantedIsUnrestricted, Boolean checkExecutionPermission)
RequestOptional(和RequestMinimum相比)有着不同的效果。改变安全声明为如下所示:
然后继续在Intranet zone执行程序集,此时会看到如下(信息):
Do not have full FileIO permission
显然程序加载成功,并执行了,但是没有被授予文件读写权限集(FileIO permission)。而这正是我期望的,在局域网上的程序集(Intranet assemblies)是不会被授予无限制文件读写权限集,而这里RequestOptional告诉CLR这段代码可以在没有这些权限集的情况下执行。通常来说,RequestOptional倾向于使用在内部,并使用try ... catch(SecurityException)块来捕捉可能存在的没有赋予权限集的问题。
到目前为止,一切顺利(So far, so good),接下来我们将着重在副作用(side effect)上,其中一条就时常困惑刚刚接触程序集安全声明的人们,直观的从字面理解,使用声明安全能够减少程序集运行的权限集,最合适的莫过于RequestRefuse了,但是当把RequestMinimum和RequestOptional放在一起(考虑)时,就不再那么直观,那么容易据理力争了。
从内部(实现)来看,CLR会取RequestMinimum和RequestOptional以及其他执行权限集的并集,最终形成唯一的权限集去控制你的程序的执行。举例来说,如果上面所述的安全声明到位的话,请求无限制的注册表权限集(unrestricted Registry permission)就会失败。
背后的原因是CLR会依据RequestMinimum和RequestOptional得到完整的,必要的程序集需要的权限集;如果你想想你所告诉运行时(runtime)的,就会有所悟了——基本上,你说的是这里需要的程序运行的最小权限集,你有可能扩展增强提供更多的其他权限集,所以这里的合并是对所有的必要的程序集而言的。限制权限集可以增加程序运行的安全性,同时也会让你无法访问你自己没有提供访问权限的资源。在上面的例子里,是没有办法通过访问注册表去攻击的。
综上所述,通常来说赋予你需要的权限集的超级会好一点(比如要求无限制文件读写就比按照特定路径的文件要求文件读写权限集要好),如果赋予的权限集是程序要求的子集会导致程序抛出SecurityExceptions。实际上,上面提到的文件读写的情况就是经常遇到的赋予超级的例子,因为通常情况下,除了运行在应用程序运行的时候,否则是不知道运行需要的文件的完整路径是什么的。另外,一个很重要的,需要注意的点是这些限制是针对单程序集的。 对某个程序集有限制的请求,能够影响应用程序中的其他程序集的唯一方法是这个程序集内部方法在调用栈上,并且在调用的时候使用权限要求(原文是:The only way that the restricted grant to this assembly could affect other assemblies in my application is if a method from this assembly happened to be on a call stack when a demand took place,这段写起来很拗,能力有限,谅解)。
至此已经没有什么大困惑了,不过还是有些小点会对你设下陷阱,以下是默认需要理解的各种重要的权限集:
- RequestMinimum -- Nothing
- RequestOptional -- FullTrust
- RequestRefuse -- Nothing
既然受限权限集是从最小要求权限集和可选权限集取并操作而来,而默认的可选权限集是FullTrust,除非设置可选权限集,否则的话,最小权限集和FullTrust求并,结果还是FullTrust权限集。这就是为什么在不指定了RequestOptional权限集,你的应用程序就不会运行在上述有限权限集的原因.
当开始明白这样做得不到比他们具体要求的更多的权限时,一个共通的问题来了——RequestRefuse有什么用处? 在我看来,对文件读写权限集最了RequestOptional 操作后再去对RegistryPermission权限集做RequestRefuse看起来傻傻的,因为根本就没有起作用。通常来说,RequestRefuse用在那些容易列出哪些权限集你一定不需要,哪些一定需要的场景.