反射发出中的安全问题

.NET Framework 提供了三种发出 Microsoft 中间语言 (MSIL) 的方式,每种方式都有其自身的安全问题:

  • 动态程序集

  • 匿名承载的动态方法

  • 与现有程序集关联的动态方法

无论您采用何种方式生成动态代码,执行生成的代码都需要生成代码使用的类型和方法所需的所有权限。

注意注意

反射和发出代码所需的权限已在 .NET Framework 的后续版本中更改。请参见本主题后面的版本信息。

动态程序集

动态程序集是使用 AppDomain.DefineDynamicAssembly 方法的重载创建的。 此方法的大多数重载在 .NET Framework 4 版中已弃用,原因是取消了计算机范围的安全策略。 (请参见 .NET Framework 4 中的安全性更改。)其余的重载均可由任意代码执行,而无论其信任级别如何。 这些重载分为两组:一组重载指定在创建动态程序集要对其应用的特性的列表,另一组重载则不会进行相应的指定。 如果没有通过在创建程序集时应用 SecurityRulesAttribute 特性来指定程序集的透明度模型,则从发出程序集继承透明度模型。

注意注意

对于在创建动态程序集之后通过使用 SetCustomAttribute 方法对其应用的特性,只有在将该程序集保存到磁盘并再次加载到内存中之后,这些特性才会起作用。

动态程序集中的代码可访问其他程序集中的可见类型和成员。

注意注意

动态程序集不使用 ReflectionPermissionFlag.MemberAccessReflectionPermissionFlag.RestrictedMemberAccess 标志,这些标志允许动态方法访问非公共类型和成员。

瞬态动态程序集在内存中创建,从不保存到磁盘中,因此它们并不需要文件访问权限。 将动态程序集保存到磁盘需要带有相应标志的 FileIOPermission

从部分受信任的代码生成动态程序集

请考虑带有 Internet 权限的程序集可以生成瞬态动态程序集并执行其代码的条件:

  • 动态程序集仅使用其他程序集的公共类型和成员。

  • 这些类型和成员所需的权限包含在部分受信任的程序集的授予集中。

  • 程序集不保存到磁盘。

  • 不生成调试符号。 (Internet 和 LocalIntranet 权限集未包括必要的权限。)

匿名承载的动态方法

通过使用未指定关联类型或模块的两个 DynamicMethod 构造函数(即 DynamicMethod(String, Type, Type[]) 和 DynamicMethod(String, Type, Type[], Boolean))来创建匿名承载的动态方法。 这些构造函数将动态方法放到系统提供的完全信任的安全透明的程序集中。 使用这些构造函数或发出动态方法的代码不需要任何权限。

相反,创建一个匿名承载的动态方法后,即捕获调用堆栈。 构造此方法时,将根据捕获的调用堆栈执行安全请求。

注意注意

从概念上来说,在方法的构造过程中执行请求。即,可在发出各 MSIL 指令时执行请求。在当前实现中,当调用 DynamicMethod.CreateDelegate 方法,或调用实时 (JIT) 编译器(如果在没有调用 CreateDelegate 的情况下调用此方法)时将执行所有请求。

如果应用程序域允许此操作,则匿名承载的动态方法可跳过 JIT 可见性检查,但受到以下限制:匿名承载的动态方法访问的非公共类型和成员必须位于这样的程序集中:其授予集等于发出调用堆栈的授予集,或等于发出调用堆栈的授予集的子集。 如果应用程序域授予带有 ReflectionPermissionFlag.RestrictedMemberAccess 标志的 ReflectionPermission,则启用此跳过 JIT 可见性检查的受限能力。

  • 如果方法仅使用公共类型和成员,则在构造过程中不需要任何权限。

  • 如果指定应跳过该 JIT 可见性检查,则在构造方法时执行的请求包括带有 ReflectionPermissionFlag.RestrictedMemberAccess 标志的 ReflectionPermission,以及包含正在访问的非公共成员的程序集的授予集。

由于考虑到非公共成员的授予集,因此被授予 ReflectionPermissionFlag.RestrictedMemberAccess 的部分受信任代码无法通过执行受信任程序集的非公共成员来提升其特权。

与任何其他发出的代码一样,执行动态方法需要动态方法使用的方法所需的所有权限。

承载匿名承载的动态方法的系统程序集使用 SecurityRuleSet.Level1 透明度模型,这是在 .NET Framework 4 之前的 .NET Framework 中使用的模型。

有关更多信息,请参见 DynamicMethod 类。

从部分受信任的代码生成匿名承载的动态方法

请考虑带有 Internet 权限的程序集可以生成匿名承载的动态方法并执行该方法的条件:

  • 动态方法仅使用公共类型和成员。 如果动态方法的授予集包含 ReflectionPermissionFlag.RestrictedMemberAccess,则动态方法可以使用任何其授予集等于发出程序集的授予集(或等于发出程序集的授予集的子集)的程序集的非公共类型和成员。

  • 动态方法使用的所有类型和成员所需的权限包含在部分受信任的程序集的授予集中。

注意注意

动态方法不支持调试符号。

与现有程序集关联的动态方法

若要将动态方法与现有程序集中的某一类型或模块关联,请使用指定关联类型或模块的任一 DynamicMethod 构造函数。 调用这些构造函数所需的权限各不相同,这是因为将动态方法与现有类型或模块关联会授予该动态方法访问非公共类型和成员的权限:

  • 与类型关联的动态方法具有对该类型的所有成员(甚至私有成员)以及包含此关联类型的程序集中的所有内部类型和成员的访问权限。

  • 与模块关联的动态方法具有对该模块中的所有 internal 类型和成员(在 Visual Basic 中为 (Friend,在公共语言运行时元数据中为 assembly)的访问权限。

此外,您可以使用一个构造函数来指定跳过 JIT 编译器的可见性检查的能力。 执行此操作可向动态方法授予对所有程序集中的所有类型和成员的访问权限,而不管访问级别如何。

构造函数所需的权限取决于您决定向动态方法授予多少访问权限:

虽然此列表中的项是用发出程序集的授予集来描述的,但是请记住,请求是根据完整的调用堆栈(包括应用程序域边界)来执行的。

有关更多信息,请参见 DynamicMethod 类。

从部分受信任的代码生成动态方法

注意注意

从部分受信任的代码生成动态方法的推荐方式是使用匿名承载的动态方法。

请考虑带有 Internet 权限的程序集可以生成动态方法并执行该方法的条件:

  • 动态方法要么与发出它的模块或类型相关联,要么其授予集中包含 ReflectionPermissionFlag.RestrictedMemberAccess,并且该方法与以下程序集中的模块关联:该程序集的授予集等于发出程序集的授予集,或者等于发出程序集的授予集的子集。

  • 动态方法仅使用公共类型和成员。 如果动态方法的授予集包含 ReflectionPermissionFlag.RestrictedMemberAccess,并且该方法与其授予集等于发出程序集的授予集(或者等于发出程序集的授予集的子集)的程序集中的模块关联,那么该方法可使用关联模块中标记为 internal 的类型和成员(在 Visual Basic 中为 Friend,在公共语言运行时元数据中为 assembly)。

  • 动态方法使用的所有类型和成员所需的权限包含在部分受信任的程序集的授予集中。

  • 动态方法不会跳过 JIT 可见性检查。

注意注意

动态方法不支持调试符号。

版本信息

从 .NET Framework 4 开始,已取消计算机范围的安全策略,并且安全透明度已成为默认的强制机制。 请参见 .NET Framework 4 中的安全性更改

从 .NET Framework 2.0 版 Service Pack 1 开始,在发出动态程序集和动态方法时不再需要带有 ReflectionPermissionFlag.ReflectionEmit 标志的 ReflectionPermission。 .NET Framework 的所有早期版本中需要此标志。

注意注意

默认情况下,带有 ReflectionPermissionFlag.ReflectionEmit 标志的 ReflectionPermission 包含在 FullTrust 和 LocalIntranet 命名权限集中,但是不包含在 Internet 权限集中。因此,在 .NET Framework 的早期版本中,仅当库执行 ReflectionEmitAssert 时才能与 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 2.0 开始,获取有关非公共类型和成员的信息将不再需要任何权限。 使用反射可获取发出动态方法所需的信息。 例如,使用 MethodInfo 对象发出方法调用。 .NET Framework 的早期版本需要带有 ReflectionPermissionFlag.TypeInformation 标志的 ReflectionPermission。 有关更多信息,请参见反射的安全注意事项

请参见

概念

反射的安全注意事项

其他资源

发出动态方法和程序集