托管代码的.NET内置安全是不可或缺的一部分,几乎所有的API调用都涉及到一些安全API。这和老式的非托管代码的工作方式有着本质的区别,况且,确保适当的地方和适当的时机的安全机制对任何应用程序都是至关重要的。
对于并非是.NET安全专家(的人来说),这里有相当多的要点要记住。
首先,通常来说,安全的概念是并非所有的编译出来的都会被执行,不同的限制适应不同的情况,例如,传统的Windows NT的安全取决于当前用户是否某些执行的基础之上的。
.NET安全(这是第二点)的决断建立在代码标识,而不是用户(这也是它命名为CAS——Code Access Security的原因)。这也意味着,为了检查某段代码是允许执行的,它需要查看如下点:代码从何处来的;有没有任何标识作者的特征(例如:授权证书);代码的散列值等等。但是这并不意味着Windows安全被重载覆盖,它依然起作用,.NET Security只是防止攻击的另一个层面。
在现实生活中,几乎有着无限多的安全配置会影响到托管代码的执行,显然是不可能涵盖所有的,不过,一些简单安全场景倒是可以完全包含的。
典型问题:
“我构建了一个需要更新注册表的应用程序,从C:\\运行它正常工作,但是从一个网络共享位置启动它就是失败。客户正在等待(回复)我该怎么做呢?”
这可能是最常见的问题了,不幸的的是对于作者来说这是预期的结果。任何来自网络(共享也被认为是网络)的代码,相对于本地代码,只获得很少的信任,例如,默认情况下是不允许访问注册表的,这是情有可原的——你为什么会让某个素未蒙面的人在你本地机器上写一些敏感信息呢?
另外一点,可能你也知道,一些Dll只允许带有已知关键字的数字签名的调用者(调用),举例来说,无论如何,除非提供证据证明你是来自Microsoft,否则将不能调用它们。由此可见,这些是很难测试的,但是至少你需要记住它们。
如何测试:
这里我假设你的专注目标不是安全方面,而是其他的,如此我们要牵出一些基本的东西来溜溜。
首先要介绍的概念是信任等级(Trust Level)。一般情况下,信任等级可以应用于程序或者函数,并且一系列允许的权限集,例如,有些代码允许对给定文件进行读写操作,无限制的网络操作,读取系统环境变量,但是不允许写注册表。
理解调用每一个API都在相应的信任等级有相应的要求这一点是很重要的,例如,如果你试图通过FileStream类去打开一个文件,首当其冲的是在做任何事情之前检查API的调用者(有权限),否则就会失败。
不同信任等级的需求量是非常多的,当然,简而言之,我们可以分成三大类:
1. 完全信任(FullTrust):在这个信任里,CAS允许做任何事情。如果某个API是需求是完全信任,那么它很有可能可以做某些相当危险的事情,一个真实的例子是Process类,它在完全信任才可以创建实例。默认的,从本地启动的一切都将获得完全信任,所以,如果你仅仅从C:\\运行你的程序,是不能真实的测试它的安全的。
2. 部分信任(Partial trust):这个真的有很多细微构成,范围涉及从显示UI的能力到通过反射调用私有方法或者文件读写。通常实现这些功能的函数要求调用者有某些相应的能力(但是没有必要完全信任,它不是“所有或者没有”之类),.NET类库中相当多的函数都有不同的部分信任需求,一般来说,来自互联网(Internet)和局域网(Intranet)的应用程序都是运行在不同程度的部分信任等级下的。 [Page]
3. 没有特殊需求(仅仅执行 (Execution only)):任何人都可以调用,因为它不代表任何安全风险,例如Math.Sqrt()这样的API。
通过上面的分类,即使不是安全专家,通过下面三步,应该能够很容易对基础级别的安全做相关测试:
1. 根据API的设计列出实际的信任等级,使用场景和测试程序。