更新:2007 年 11 月
.NET Framework 提供了三种发出 Microsoft 中间语言 (MSIL) 的方式,每种方式都有其自身的安全问题:
动态程序集
匿名承载的动态方法
与现有程序集关联的动态方法
无论您采用何种方式生成动态代码,执行生成的代码都需要生成代码使用的类型和方法所需的所有权限。
![]() |
---|
反射和发出代码所需的权限已在 .NET Framework 的后续版本中更改。请参见本主题后面的版本信息。 |
动态程序集
动态程序集是使用 AppDomain.DefineDynamicAssembly 方法的重载创建的。所需的权限取决于使用的重载。例如,提供证据的重载需要带有 SecurityPermissionFlag.ControlEvidence 标志的 SecurityPermission。某些重载不需要任何权限,可从只有 Internet 权限的代码进行调用。
动态程序集中的代码可访问其他程序集中的可见类型和成员。
![]() |
---|
动态程序集不使用 ReflectionPermissionFlag.MemberAccess 和 ReflectionPermissionFlag.RestrictedMemberAccess 标志,这些标志允许动态方法访问非公共类型和成员。 |
生成带有调试符号的代码需要带有 ReflectionPermissionFlag.ReflectionEmit 标志的 ReflectionPermission。
瞬态动态程序集在内存中创建,从不保存到磁盘中,因此它们并不需要文件访问权限。将动态程序集保存到磁盘需要带有相应标志的 FileIOPermission。
从部分受信任的代码生成动态程序集
请考虑带有 Internet 权限的程序集可以生成瞬态动态程序集并执行其代码的条件:
动态程序集仅使用其他程序集的公共类型和成员。
这些类型和成员所需的权限包含在部分受信任的程序集的授予集中。
用于创建动态程序集的 DefineDynamicAssembly 方法重载不需要特殊权限(即未提供证据等)。
程序集不保存到磁盘。
不生成调试符号。(Internet 和 LocalIntranet 权限集不包含 ReflectionPermissionFlag.ReflectionEmit 标志。)
建立动态程序集的权限
在下面的列表中,“发射器”是生成动态程序集的程序集。
具有 SecurityPermission(带有 SecurityPermissionFlag.ControlEvidence 标志)的发射器可为生成的代码提供证据。此证据通过策略映射以确定授予的权限。
发射器可以提供空证据,这种情况下程序集获取发射器的权限集。这可确保生成的代码具有的权限不会比其发射器更多。
如果提供具有 SecurityAction.RequestMinimum、SecurityAction.RequestOptional 或 SecurityAction.RequestRefuse 的权限集,则不会使用这些权限集,直到将程序集保存到磁盘并从磁盘加载。
将动态程序集保存到磁盘之后,该程序集的处理方式与磁盘中加载的任何其他程序集相似。
总是验证由不完全受信任的发射器生成的代码。具体来说,运行库总是验证没有 SecurityPermission(带有 SecurityPermissionFlag.SkipVerification 标志)的代码。完全受信任的发射器可以跳过验证或要求验证生成的代码。
匿名承载的动态方法
通过使用未指定关联类型或模块的两个 DynamicMethod 构造函数(即 DynamicMethod(String, Type, array<Type[]) 和 DynamicMethod(String, Type, array<Type[], Boolean))来创建匿名承载的动态方法。这些构造函数将动态方法放到系统提供的完全信任的安全透明的程序集中。使用这些构造函数或发出动态方法的代码不需要任何权限。
相反,创建一个匿名承载的动态方法后,即捕获调用堆栈。构造此方法时,将根据捕获的调用堆栈执行安全请求。
![]() |
---|
从概念上来说,在方法的构造过程中执行请求。即,可在发出各 MSIL 指令时执行请求。在当前实现中,当调用 DynamicMethod.CreateDelegate 方法,或调用实时 (JIT) 编译器(如果在没有调用 CreateDelegate 的情况下调用此方法)时将执行所有请求。 |
匿名承载的动态方法可跳过 JIT 可见性检查,但受到以下限制:匿名承载的动态方法访问的非公共类型和成员必须位于这样的程序集中:其授予集等于发出调用堆栈的授予集,或等于发出调用堆栈的授予集的子集。此跳过 JIT 可见性检查的受限能力需要带有 ReflectionPermissionFlag.RestrictedMemberAccess 标志的 ReflectionPermission。
如果方法仅使用公共类型和成员,则在构造过程中不需要任何权限。
如果指定应跳过该 JIT 可见性检查,则在构造方法时执行的请求包括带有 ReflectionPermissionFlag.RestrictedMemberAccess 标志的 ReflectionPermission,以及包含正在访问的非公共成员的程序集的授予集。
由于考虑到非公共成员的授予集,因此被授予 ReflectionPermissionFlag.RestrictedMemberAccess 的部分受信任代码无法通过执行受信任程序集的非公共成员来提升其特权。
与任何其他发出的代码一样,执行动态方法需要动态方法使用的方法所需的所有权限。
有关更多信息,请参见 DynamicMethod 类。
从部分受信任的代码生成匿名承载的动态方法
请考虑带有 Internet 权限的程序集可以生成匿名承载的动态方法并执行该方法的条件:
动态方法仅使用公共类型和成员。如果动态方法的授予集包含 ReflectionPermissionFlag.RestrictedMemberAccess,则动态方法可以使用任何其授予集等于发出程序集的授予集(或等于发出程序集的授予集的子集)的程序集的非公共类型和成员。
动态方法使用的所有类型和成员所需的权限包含在部分受信任的程序集的授予集中。
![]() |
---|
动态方法不支持调试符号。 |
与现有程序集关联的动态方法
若要将动态方法与现有程序集中的某一类型或模块关联,请使用指定关联类型或模块的任一 DynamicMethod 构造函数。调用这些构造函数所需的权限各不相同,这是因为将动态方法与现有类型或模块关联会授予该动态方法访问非公共类型和成员的权限:
与类型关联的动态方法具有对该类型的所有成员(甚至私有成员)以及包含此关联类型的程序集中的所有内部类型和成员的访问权限。
与模块关联的动态方法具有对该模块中的所有 internal 类型和成员(在 Visual Basic 中为 (Friend,在公共语言运行库元数据中为 assembly)的访问权限。
此外,您可以使用一个构造函数来指定跳过 JIT 编译器的可见性检查的能力。执行此操作可向动态方法授予对所有程序集中的所有类型和成员的访问权限,而不管访问级别如何。
构造函数所需的权限取决于您决定向动态方法授予多少访问权限:
如果方法仅使用公共类型和成员,并且您将该方法与您自己的类型或模块相关联,则不需要任何权限。
如果指定应跳过 JIT 可见性检查,则构造函数需要带有 ReflectionPermissionFlag.MemberAccess 标志的 ReflectionPermission。
如果将动态方法与另一类型(甚至是您自己的程序集中的另一类型)关联,则构造函数需要带有 ReflectionPermissionFlag.MemberAccess 标志的 ReflectionPermission 和带有 SecurityPermissionFlag.ControlEvidence 标志的 SecurityPermission。
如果将动态方法与其他程序集中的类型或模块关联,构造函数可能需要下列两项:带有 ReflectionPermissionFlag.RestrictedMemberAccess 标志的 ReflectionPermission 和包含其他模块的程序集的授予集。也就是说,调用堆栈必须包含目标模块的授予集中的所有权限以及 ReflectionPermissionFlag.RestrictedMemberAccess。
说明:
为实现向后兼容性,如果对目标授予集和 ReflectionPermissionFlag.RestrictedMemberAccess 的请求失败,构造函数将需要带有 SecurityPermissionFlag.ControlEvidence 标志的 SecurityPermission。
虽然此列表中的项是用发出程序集的授予集来描述的,但是请记住,请求是根据完整的调用堆栈(包括应用程序域边界)来执行的。
有关更多信息,请参见 DynamicMethod 类。
从部分受信任的代码生成动态方法
![]() |
---|
从部分受信任的代码生成动态方法的推荐方式是使用匿名承载的动态方法。 |
请考虑带有 Internet 权限的程序集可以生成动态方法并执行该方法的条件:
动态方法要么与发出它的模块或类型相关联,要么其授予集中包含 ReflectionPermissionFlag.RestrictedMemberAccess,并且该方法与以下程序集中的模块关联:该程序集的授予集等于发出程序集的授予集,或者等于发出程序集的授予集的子集。
动态方法仅使用公共类型和成员。如果动态方法的授予集包含 ReflectionPermissionFlag.RestrictedMemberAccess,并且该方法与其授予集等于发出程序集的授予集(或者等于发出程序集的授予集的子集)的程序集中的模块关联,那么该方法可使用关联模块中标记为 internal 的类型和成员(在 Visual Basic 中为 Friend,在公共语言运行库元数据中为 assembly)。
动态方法使用的所有类型和成员所需的权限包含在部分受信任的程序集的授予集中。
动态方法不会跳过 JIT 可见性检查。
![]() |
---|
动态方法不支持调试符号。 |
版本信息
从 .NET Framework 2.0 Service Pack 1 开始,在发出动态程序集和动态方法时不再需要带有 ReflectionPermissionFlag.ReflectionEmit 标志的 ReflectionPermission。.NET Framework 的所有早期版本中需要此标志。
![]() |
---|
默认情况下,带有 ReflectionPermissionFlag.ReflectionEmit 标志的 ReflectionPermission 包含在 FullTrust 和 LocalIntranet 命名的权限集中,但是不包含在 Internet 权限集中。因此,在 .NET Framework 的早期版本中,仅当库执行 ReflectionEmit 的 Assert 时才能与 Internet 权限一起使用。这种库需要进行仔细的安全检查,因为编码错误可能会导致安全漏洞。.NET Framework 2.0 SP1 允许在部分信任的方案中发出代码而无需发出任何安全请求,因为生成代码本身不是一项特权操作。也就是说,生成的代码不会具有比发出它的程序集更多的权限。这使得发出代码的库是安全透明的,且不再需要断言 ReflectionEmit,这简化了编写安全库任务。 |
此外,.NET Framework 2.0 SP1 引入了 ReflectionPermissionFlag.RestrictedMemberAccess 标志,用于从部分受信任的动态方法访问非公共类型和成员。.NET Framework 的早期版本需要访问非公共类型和成员的动态方法的 ReflectionPermissionFlag.MemberAccess 标志;绝不会将该权限授予部分受信任的代码。
最后,.NET Framework 2.0 SP1 引入了匿名承载的方法。
若要使用这些功能,应用程序应当面向 .NET Framework 3.5 版。有关更多信息,请参见 .NET Framework 3.5 体系结构。
获取类型和成员的信息
从 .NET Framework 2.0 开始,获取有关非公共类型和成员的信息将不再需要任何权限。使用反射可获取发出动态方法所需的信息。例如,使用 MethodInfo 对象发出方法调用。.NET Framework 的早期版本需要带有 ReflectionPermissionFlag.TypeInformation 标志的 ReflectionPermission。有关更多信息,请参见反射的安全注意事项。