摘要:本文概述了 Microsoft .NET 框架安全系统的基本特性,包括对于动态下载和执行模式以及远程执行模式,限制代码在严格约束的、管理员定义的安全上下文中运行的能力。(21页打印页)
Demien Watkins 博士,项目 42Sebastian Lange,Microsoft Corporation
2002 年 1 月
本页内容
简介 | |
执行概述 | |
验证 | |
代码访问安全性 | |
权限 | |
证据 | |
安全策略 | |
堆栈审核 | |
声明性方式和命令性方式 | |
忠告 | |
基于角色的安全性 | |
小结 | |
致谢 | |
附录 A:MsCorCfg.msc |
? | 无效 MSIL 是 JIT 编译器不能为它生成本机表示的 MSIL。例如,不可将包含无效操作码的 MSIL 翻译成本机代码。另一个示例是跳转指令,该指令的目标是操作数的地址,而不是操作码。 |
? | 有效 MSIL 可以被认为是满足 MSIL 语法的所有 MSIL,因此它可以用本机代码表示。这种分类包括这样的 MSIL,即,使用非类型安全形式的指针算法获取对类型成员的访问的 MSIL。 |
? | 类型安全 MSIL 只通过它们的向公众公开的协定与类型交互。试图从一个类型中访问另一个类型的私有成员的 MSIL 就不是类型安全的。 |
? | 可验证 MSIL 是可以通过一个验证算法来证明是类型安全的 MSIL。验证算法比较保守,因此一些类型安全的 MSIL 可能不会通过验证。可验证 MSIL 自然既是类型安全的又是有效的,当然不是无效的。 |
除了类型安全检查之外,运行库中的 MSIL 验证算法还会检查堆栈上溢/下溢的发生、异常处理工具的正确使用以及对象初始化。
对于从磁盘加载的代码,验证过程是 JIT 编译器的一部分,它会在 JIT 编译器中间歇性地进行。验证和 JIT 编译不是作为两个独立的进程来执行的。如果验证期间在程序集中找到了一连串不可验证的 MSIL,那么安全系统就会检查该程序是否足够受信任,可以跳过验证。例如,如果一个程序集是在安全模型的默认设置下从本地硬盘上加载的,那可能就是这样的情况。如果程序集受信任可跳过验证,MSIL 则会翻译成本机代码。如果程序集的受信任程度不够,不可跳过验证,则会用一个存根来代替有问题的 MSIL,如果使用了该执行路径,该存根就会引发一个异常。一个常见的问题是:“为什么不在验证程序集之前检查它是否需要验证呢?”因为验证通常是作为 JIT 编译的一部分执行的,所以它通常比检查是否允许程序集跳过验证更快。(决定跳过验证比这里描述的过程更加智能。例如,可以缓存前面一些验证尝试的结果,以提供快速的查找方案。)
除了 MSIL 验证之外,还要验证程序集元数据。事实上,类型安全依赖于这些元数据检查,因为它假定 MSIL 验证期间使用的元数据标记是正确的。在全局程序集缓存 (GAC) 或下载缓存中加载程序集时,会验证程序集元数据;如果没有将它插入到 GAC 中,则从磁盘中读取它时也会验证程序集元数据。(GAC 是由一些程序使用的程序集的中央存储。下载缓存保存了从其他位置(如 Internet)下载的程序集。)元数据验证包括检查元数据标记和消除缓冲区溢出,前者用于检查它们是否会正确索引到它们访问的表中,以及到字符串表的索引是否并不指向长度大于应该保存它们的缓冲区大小的字符串。通过 MSIL 验证和元数据验证消除非类型安全的类型安全代码是运行库上安全性的第一部分。
站点:代码来自的站点。URL 比站点的概念更具体;例如 www.microsoft.com 就是一个站点。
应用程序目录:要从中加载代码的目录。
发行者证书:程序集的 Authenticode 数字签名。
注 从理论上讲,任何托管对象都可以构成证据。上面只是一些在 .NET 框架中具有对应成员条件的类型,因此可以将它们集成到安全策略中,而不必编写自定义安全对象。有关安全策略和代码组的详细信息,请参阅下面的内容。
下面的程序是证据的一个简单示例,其中证据是在加载程序集时被传递到运行库安全系统的。本例中,mscorlib 是加载的程序集,它是包含了许多运行库类型(如 Object 和 String)的程序集。
using System;using System.Collections;using System.Reflection;using System.Security.Policy;namespace AssemblyEvidence{class Class1{static void Main(string[] args){Type t = Type.GetType("System.String");Assembly a = Assembly.GetAssembly(t);Evidence e = a.Evidence;IEnumerator i = e.GetEnumerator();while(i.MoveNext())Console.WriteLine(i.Current);}}}
程序的输出显示了对于此程序集向安全系统传递了哪些证据。为简洁起见,已对下面的输出进行了编辑。安全系统采用这个证据,然后根据管理员设置的安全策略为该程序集生成了一个权限集。
<System.Security.Policy.Zone version="1"><Zone>MyComputer</Zone></System.Security.Policy.Zone><System.Security.Policy.Url version="1"><Url>file:///C:/winnt/microsoft.net/framework/v1.0.2728/mscorlib.dll</Url></System.Security.Policy.Url><StrongName version="1"Key="00000000000000000400000000000000"Name="mscorlib"Version="1.0.2411.0"/><System.Security.Policy.Hash version="1"><RawData>4D5A90000300000004000000FFFF0000B8000000000000...0000000000000000000000000000000000000000000000000000</RawData></System.Security.Policy.Hash>
决定授予程序集一个实际权限集的过程包括三个步骤:
1.各个策略级别计算程序集的证据,然后生成特定于策略级别的授予权限集。
2.为每个策略级别计算的权限集彼此进行相交。
3.得到的权限集与程序集声明的运行所需的权限集或拒绝权限进行比较,然后相应地修改权限的授予。
图 1. 一个程序集的授予权限集的计算
图 1 显示了大概的计算过程。运行库的宿主提供有关程序集的证据,该证据作为程序集收到权限集的计算的一个输入。可管理的安全策略(企业策略、计算机策略和用户策略)是决定程序集的权限集计算的第二个输入,本文前面称之为安全性设置。然后安全策略代码(包含在 SecurityManager 类中)遍历各个策略级别设置提供的程序集证据,然后生成一个权限集,它代表了该程序集可访问受保护资源的权限集合。
每个策略级别是如何管理的呢?策略级别表示一个独立、可配置的安全策略单元 – 每个级别都将程序集证据映射到一个权限集。每个策略级别都具有类似的结构。每个策略级别都由三个部分组成,这三个部分组合在一起用来表示策略级别的配置状态:
? | 代码组树 |
? | 命名权限列表 |
? | 策略程序集列表 |
现在我们来详细阐述所有策略级别的这些组成部分。
代码组
每个策略级别的核心是代码组树。它表示策略级别的配置状态。代码组实质是一个条件表达式和一个权限集。如果程序集满足该条件表达式,那么就会被授予该权限集。每个策略级别的代码组集是按树的形式组织的。一旦条件表达式的计算结果为真,就会授予该权限集,然后继续在那个分支中遍历。只要不满足条件,就不授予权限集,也就不再进一步检查那个分支。例如,有一个代码组树,它是按照下面的这种情况运行的。
图 2. 策略级别的代码组树
注 这里我们只讨论用于实现默认安全策略的代码组类型的代码组语义。也可以包括自定义编写的、语义完全不同于这里所描述语义的代码组。再提一下,安全系统是完全可以扩展的,因此它为引入新的策略计算语义提供了无限的可能性。
假设有一个程序集,它具有下列证据:它来自 www.monash.edu.au,因为它来自 Monash University 的 m-Commerce Center,所以它的强名称为 mCommerce。
代码组树遍历则会按照下列方式进行:
根节点有一个任何代码都满足的条件 "all co
最终,满足的条件和从这个策略级别中授予的权限集包括:
? | 条件:All co |
? | 条件:Zone:Internet,权限集:Internet |
? | 条件:URL:www.monash.edu.au,权限集:MonashPSet |
? | 条件:Strong Name:m-Commerce,权限集:m-CommercePSet |
在一个策略级别找到的适用于某个特定程序集的所有权限集通常会进行联合,以生成该策略级别授予的总权限集。
检查一个策略级别的代码组树是非常简单的。附录 A 描述了一个 Microsoft 管理控制台单元,该单元提供了一个用于查看和修改代码组的层次结构(以及策略级别的其他所有可配置组成部分,请参阅下面的内容)的可视界面。
命名的权限集
一个策略级别包含一个命令权限集列表。每个权限集代表着一个信任声明,用于访问各种受保护资源。命名的权限集是代码组按其名称进行引用的一些权限集。如果满足了代码组的条件,那么就授予被引用的命名权限集(请参阅上面的示例)。下面是一些预定义的命名权限集示例:
? | FullTrust:允许不受限制地访问系统资源。 |
? | SkipVerification:可以跳过验证的程序集。 |
? | Execution:允许代码执行。 |
? | Nothing:不授予权限。不授予执行的权限可有效停止代码的运行。 |
? | Internet: 适合来自 Internet 的代码的权限集。代码将不能收到对于文件系统或注册表的访问权限,但可以执行一些有限的用户界面操作,并且可以使用称为独立存储的安全文件系统。 |
为了查看策略级别的权限集,只要在附录 A 提及的 GUI 工具中打开策略级别节点,然后打开权限集文件夹即可。
下面是一个很小的示例程序,它列出了所有策略级别上所有已知的命名权限集:
下面的程序显示了所有策略级别上的命名权限集列表。该应用程序是一个从本地磁盘上运行的 C# 程序,因此它会从默认的策略设置中收到一个相当强大的权限集。
using System;using System.Collections;using System.Security;using System.Security.Policy;namespace SecurityResolver{class Sample{static void Main(string[] args){IEnumerator i = SecurityManager.PolicyHierarchy();while(i.MoveNext()){PolicyLevel p = (PolicyLevel) i.Current;Console.WriteLine(p.Label);IEnumerator np = p.NamedPermissionSets.GetEnumerator();while (np.MoveNext()) {NamedPermissionSet pset = (NamedPermissionSet)np.Current;Console.WriteLine("\tPermission Set: \n\t\t Name: {0}\n\t\t Description {1}", pset.Name, pset.Description);}}}}}
该程序的输出。为简洁和明确起见,已对该输出进行了编辑。
EnterprisePermission Set:Name: FullTrustDescription: Allows full access to all resourcesPermission Set:Name: LocalIntranetDescription: Default rights given to applications on your local intranet...MachinePermission Set:Name: NothingDescription: Denies all resources, including the right to execute...User...Name: SkipVerificationDescription: Grants right to bypass the verificationPermission Set:Name: ExecutionDescription: Permits execution...
策略程序集
在安全计算期间,可能需要加载其他的程序集以在策略计算过程中使用。例如,一个程序集可以包含代码组发出权限集的用户定义权限类部分。当然也需要计算这个包含自定义权限的程序集。如果这个自定义权限的程序集被授予包含它自己实现的自定义权限的权限集,那么就会引发循环依赖项。为了避免这种情况,每个策略级别都包含了一个它在策略计算中所需的受信任程序集列表。这个必需程序集的列表自然地被称为了“策略程序集”列表,它包含了在这个策略级别实现安全策略所需的所有程序集的过渡闭包。包含在该列表中的所有程序集的策略计算是短路的,以避免发生循环依赖项。该列表可以使用附录 A 提及的GUI 管理工具来修改。
这就完成了每个策略级别上的可配置组成部分的检查:代码组树、命名权限集列表和策略程序集列表。现在该来看一下从安全策略状态派生的授予权限集是如何在由每个策略级别的配置实例化时,与安全强制的基础结构进行连接的。换言之,迄今为止,我们只讨论了程序集是如何收到授予的权限集的。如果没有一个基础结构要求程序集具有某一级别的权限,那么安全系统将不能发挥任何作用。事实上,使安全强制成为可能的技术是安全堆栈审核。
附录 A:MsCorCfg.msc
有一个 Microsoft 管理控制台单元使我们能够可视化地操纵代码访问安全性策略。下图显示了这个管理单元的界面,其中突出显示了本文论及的一些概念。
图 5. Microsoft 管理控制台单元界面
您可以在 Control Panel 中单击 Administrative Tools,然后单击 Microsoft .NET Framework Configuration 快捷键,来访问该工具。
评论