对于像CLR这样被虚拟化的(virtualized)执行环境,它的一个优点就是,你可以超越底层操作系统的安全模型丌发出新的安全模型。CLR实现了独立于宿主平台的自己的安全执行模型:它的出现,除了为那些以前不很安全的平台(例如,Windows 98)带来安全性外,还为实施以组件为中心(component-centric)的安全模型提供了良机,这种安全模型尤其适合动态组成系统的本性。这种以组件为中心的安全模型被称为代码访问安全性(code-access security,CAS),这是本章讨论的重点。
组件和安全性由组件动态组成的系统对安全有着独特的要求。由于应用程序的组件个体经常可能来源于不同的组织,因而应用程序的不同方面可能需要不同程度的信任。例如,来自受信任组织的组件,可能需要访问私有信息或者关键资源,而这些资源则要求防范恶意代码的攻击。遗憾的是,Windows NT和UNIX中基于密码的(principal-based)传统安全模型,忽略了代码的来源,而只是关注谁执行这些代码。对于20世纪80年代创建的程序,也就是组件普及之前,这种模型还有一定意义。然而,对于以组件为中心的世界,应用程序的代码可能来自全世界的各个角落,这种安全模型就显得过于粗糙。因此,代码访问安全也就应运而生。
CLR实现了代码访问安仝模型,在这个模型中,特权被授予代码而不是用户。在加载一个程序集之前,CLR将收集该代码来源的证据(evidence)。CLR将程序集的内存表示和证据关联在一起,而安全系统将使用它决定授予什么特权给新加载的代码。CLR通过一个安全策略(security policy)运行该证据,从而作出这个决定。安全策略接收证据作为输八,并产生一个权限集(permission set)作为输出,为了避免过度的性能开销,系统通常到有显式的安全要求时,才执行安全策略。
所谓权限集就是权限的一个集台:例如,由策略返回的那些权限。权限(permission)就是执行某种受信任操作的权利。CLR本身具有一套内置的(built-in)权限类型,用于保护某个系统的一致性和用户的隐私,不过,这个系统是可扩展的,你可以透明地将用户自定义权限类型集成到达个模型中。
关于代码被赋予哪些权限的决定称为策略(policy)。CLR使用一套独特的机制实施安全策略。在执一个特权操作以前,受信任代码通过显式地要求调用方(callers)具有足够的执行该操作的权限,实施安生策略。在这里需要注意“callers”复数形式的使用。默认情形下,策略执行将要求所有直接的或者间接的调用方具有足够的权限执行这个特权操作。这就防止了未
受信任的组件引诱那些容易上当受骗的(但却是受信任的)代码片断,去执行某个操作,从而盗用了那些代码片断的特权。
与垃圾回收(garbage collection) 一样,代码访问安全性需要一个无所不知,无所不能的运行时。这意味着当调用没有以严格的格式书写的代码时,就可能会对系统安全产生障碍。为此,CLI把代码分成两大类别:可验证代码(verifiable code)和不可验证代码(non-verifiable code)。可验证代码可被精确地证明将遵从CLI提倡的类型安全执行模型。VB.NET和c#默认生成可验证的代码。然而,也有某些语言的特征不能被验证是类型安全的,c++的reinterpret_cast就是一个典型的例子。正是因为这个原因,通过C++编译器发射的代码显然是不可验证的,使用这种代码可能会损害系统的安全性。为了保护系统的一致性,加载不可验证代码的能力本身就是一个权限。必须通过策略显式地将它授予代码。随CLR一起安装的默认策略,仅仅对安装在本地文件系统上的代码授予了这种权跟。类似地,调用基于c或COM的传统DLL(从定义上说是不可校验的)也是一个需要信任的操作,默认只授予安装在本地文件系统中的代码。
一般来说,代码访问安全性对性能的影响是微不足道的。程序集加载器(assembly loader)将做一些附加工作以收集证据,然而,这只是在加载时发生,并且在内存中其开销是在程序集整个生存期内分摊的。关键的性能主要集中在策略实施上,它将导致对于整个调用堆栈的潜在遍历。有幸的是,你可以在设计中分解安全性,最低限度地减少策略实施的开销,典型的做法就是通过显式的编程技巧,避免过度的策略实施。这些技巧将在本章后面讨论。
证据我们将由证据开始讨论代码访问安全性的问题。证据充当有关给定代码片断来源的证词。程序集加载器负责在加载时收集证据,这些证据既取决于代码的来源,又取决于程序集自身的元数据。
CLR提供了七种类型的证据。这些证据类型有四种(Site、URL、Zone和
ApplicationDirectory)与代码从哪里加载相关。有两种证摒类型(StrongName和Publisher)与谁编写该代码相关。第七种证据类型Hash,是基于程序集的全部内容,并且允许你侦测一段代码的特定编译,它与程序集的版本号无关。
整体来说,这七种证据类型被称为宿主证据(host evidence),因为它们由宿主环境所实现。你也可以定义自己的证据类型,它们通常称为程序集征据(assembly evidence),因为它们由程序集自身显式地提供。定义新的程序集类型还需要扩展策略机制以识别它们,但这超出了本书的范围。本章的剩余部分主要是关于内置的宿主证据类型的讨论。
我们还记得程序集加载器最终是根据codebase(基本代码)URL来工作的.其中一些可能是基于文件的。CLR使用codebase URL决定四种基于位置证据类型中的三种:URL、Zone和Site。URL证据是最容易理解的,因为它就是原始形式的codebase URL。Site和zone证据类型是以codebase的URL的内容为基础,并由它派生而来的。
Site证据类型只是一个基于HTTP或者FTP的URL宿主名的一部分。例如,如果一个程序集的codebase是http://www.acme.com/foo.dLl,那么它的site是www.acme.com。然而,如果codebase是基于文件的URL(例如.fill:///c://usr/bin/foo.dll),那么,对于程序集来说就没有Site证据。Site证据类型通常用于对从受信任库中下载的代码授予信任。
Zone证据类型也派生于基本代码URL。CLR把世界分成五种可能的安全区域,这由System.Security.SecurityZone枚举表示:
namespace System.Security{
public enum SecurityZone{
MyComputer,
Intranet,
Trusted,
Internet,
Untrusted,
NoZone=0xFFFFFFF
}
}
MyComputer区域适用于从本地文件系统加载的所有代码。CLR将来自远程文件系统的代码进行分类,这取决于Internet资源管理器(InternetExplorer)的Internet选项对话框的设置。S翂 0僛>f_0WR鶴哊