.NET Framework 提供了三种方法来发出公共中间语言 (CIL),每个语言都有其自己的安全问题:
无论生成动态代码的方式如何,执行生成的代码都需要生成代码所使用的类型和方法所需的所有权限。
注释
反映代码和发出代码所需的权限已随着 .NET Framework 的后续版本而更改。 请参阅本文后面的 版本信息。
动态程序集
通过使用AppDomain.DefineDynamicAssembly方法的重载可以创建动态程序集。 由于消除了系统范围的安全策略,此方法的大多数重载在 .NET Framework 4 中已弃用。 无论信任级别如何,其余重载都可以由任何代码执行。 这些重载分为两个组:一组在创建动态程序集时指定要应用的属性列表,另一组则不指定。 如果您未在创建程序集时指定其透明度模型,则通过应用 SecurityRulesAttribute 属性,该透明度模型将从发出程序集继承。
注释
使用 SetCustomAttribute 该方法创建动态程序集后应用于动态程序集的属性不会生效,直到程序集保存到磁盘并再次加载到内存中。
动态程序集中的代码可以访问其他程序集中的可见类型和成员。
注释
动态程序集不使用 ReflectionPermissionFlag.MemberAccess 和 ReflectionPermissionFlag.RestrictedMemberAccess 标志,这些标志允许动态方法访问非公共类型和成员。
暂时性动态程序集在内存中创建,永远不会保存到磁盘,因此不需要文件访问权限。 将动态程序集保存到磁盘需要带有相应标志的 FileIOPermission。
从部分受信任的代码生成动态程序集
请考虑具有 Internet 权限的程序集可以生成暂时性动态程序集并执行其代码的条件:
动态程序集仅使用其他程序集的公共类型和成员。
这些类型和成员所需的权限包含在部分受信任的程序集的授予集中。
程序集不会保存到磁盘。
不会生成调试符号。 (
Internet
和LocalIntranet
权限集不包括必要的权限。
匿名托管的动态方法
匿名托管的动态方法是使用 DynamicMethod 两个未指定关联类型或模块的构造函数创建的, DynamicMethod(String, Type, Type[]) 以及 DynamicMethod(String, Type, Type[], Boolean)。 这些构造函数将动态方法置于系统提供的高度信任、并且安全透明的程序集。 使用这些构造函数或发出动态方法的代码不需要任何权限。
相反,在创建匿名托管的动态方法时,将捕获调用堆栈。 构造该方法时,针对捕获的调用堆栈发出安全要求。
注释
从概念上讲,在方法的构造过程中提出了要求。 即,可在发出各 CIL 指令时执行请求。 在当前实现中,当调用 DynamicMethod.CreateDelegate 方法,或调用实时 (JIT) 编译器(如果在没有调用 CreateDelegate 的情况下调用此方法)时将执行所有请求。
如果应用程序域允许它,匿名托管的动态方法可以跳过 JIT 可见性检查,但受以下限制的约束:匿名托管动态方法访问的非公共类型和成员必须位于授予集等于发出调用堆栈的授予集或子集的程序集中。 如果应用程序域授予带有 ReflectionPermission 标志的 ReflectionPermissionFlag.RestrictedMemberAccess,则启用此跳过 JIT 可见性检查的受限能力。
如果方法仅使用公共类型和成员,则构造过程中不需要任何权限。
如果指定应跳过 JIT 可见性检查,则在构造方法时执行的请求包括带有 ReflectionPermission 标志的 ReflectionPermissionFlag.RestrictedMemberAccess,以及包含正在访问的非公共成员的程序集的授予集。
由于考虑了非公共成员的授予权限集,因此已授予 ReflectionPermissionFlag.RestrictedMemberAccess 的部分信任的代码无法通过执行受信任程序集的非公共成员来提升权限。
与任何其他发出的代码一样,执行动态方法需要动态方法使用的方法所需的任何权限。
承载匿名托管动态方法的系统程序集使用 SecurityRuleSet.Level1 透明度模型,这是 .NET Framework 4 之前在 .NET Framework 中使用的透明度模型。
有关更多信息,请参见 DynamicMethod 类。
从部分受信任的代码生成匿名托管的动态方法
请考虑具有 Internet 权限的程序集可以生成匿名托管的动态方法并执行它的条件:
动态方法仅使用公共类型和成员。 如果动态方法的授予集包含 ReflectionPermissionFlag.RestrictedMemberAccess,则动态方法可以使用任何其授予集等于发出程序集的授予集(或等于发出程序集的授予集的子集)的程序集的非公共类型和成员。
动态方法所使用的所有类型和成员所需权限均包含在部分信任程序集的授权集中。
注释
动态方法不支持调试符号。
与现有程序集关联的动态方法
若要将动态方法与现有程序集中的类型或模块相关联,请使用指定关联类型或模块的任何 DynamicMethod 构造函数。 调用这些构造函数所需的权限会有所不同,因为将动态方法与现有类型或模块相关联可授予对非公共类型和成员的动态方法访问权限:
与类型关联的动态方法有权访问该类型的所有成员,甚至是私有成员,以及包含关联类型的程序集中的所有内部类型和成员。
与模块关联的动态方法可以访问模块中的所有
internal
类型和成员(Friend
在 Visual Basic 中,assembly
公共语言运行时元数据)。
此外,还可以使用指定跳过 JIT 编译器可见性检查的功能的构造函数。 这样做可使你的动态方法能够访问所有程序集中的所有类型和成员,而不受访问级别的限制。
构造函数所需的权限取决于你决定为动态方法提供多少访问权限:
如果方法仅使用公共类型和成员,并且将其与自己的类型或你自己的模块相关联,则无需任何权限。
如果指定应跳过 JIT 可见性检查,则构造函数需要带有 ReflectionPermission 标志的 ReflectionPermissionFlag.MemberAccess。
如果将动态方法与另一类型(甚至是你自己的程序集中的另一类型)关联,则构造函数需要带有 ReflectionPermission 标志的 ReflectionPermissionFlag.MemberAccess 和带有 SecurityPermission 标志的 SecurityPermissionFlag.ControlEvidence。
如果将动态方法与另一个程序集中的类型或模块相关联,那么构造函数要求两项内容:ReflectionPermission 具有 ReflectionPermissionFlag.RestrictedMemberAccess 标志,以及包含该模块的程序集的权限集。 也就是说,调用堆栈必须包括目标模块的授权集中的所有权限以及 ReflectionPermissionFlag.RestrictedMemberAccess。
注释
为实现向后兼容性,如果对目标授予集和 ReflectionPermissionFlag.RestrictedMemberAccess 的请求失败,构造函数将需要带有 SecurityPermission 标志的 SecurityPermissionFlag.ControlEvidence。
虽然此列表中的项是用发出程序集的授予集来描述的,但是请记住,请求是根据完整的调用堆栈(包括应用程序域边界)来执行的。
有关更多信息,请参见 DynamicMethod 类。
从部分受信任的代码生成动态方法
注释
从部分受信任的代码生成动态方法的建议方法是使用 匿名托管的动态方法。
请考虑具有 Internet 权限的程序集可以生成动态方法并执行它的条件:
动态方法要么与发出它的模块或类型相关联,要么它的授权集包括 ReflectionPermissionFlag.RestrictedMemberAccess 并且与一个模块相关联,该模块位于一个程序集内,该程序集的授权集等于或是发出程序集的授权集的子集。
动态方法仅使用公共类型和成员。 如果其授权集包括 ReflectionPermissionFlag.RestrictedMemberAccess 并且它与一个模块相关联,该模块在一个程序集中的授权集等于或是发出程序集授权集的子集,那么它可以在关联的模块中使用那些标记为
internal
(在 Visual Basic 中是Friend
,在公共语言运行时元数据中是assembly
)的类型和成员。动态方法使用的所有类型和成员所需的权限包含在部分受信任的程序集的授予集中。
动态方法不会跳过 JIT 可见性检查。
注释
动态方法不支持调试符号。
版本信息
从 .NET Framework 4 开始,将消除计算机范围的安全策略,安全透明度成为默认强制机制。
从 .NET Framework 2.0 Service Pack 1 开始,在发出动态程序集和动态方法时,不再需要使用带有 ReflectionPermission 标志的 ReflectionPermissionFlag.ReflectionEmit 。 所有早期版本的 .NET Framework 都需要此标志。
注释
默认情况下,带有 ReflectionPermission 标志的 ReflectionPermissionFlag.ReflectionEmit 包含在 FullTrust
和 LocalIntranet
命名权限集中,而不是在 Internet
权限集中。 因此,在 .NET Framework 的早期版本中,仅当库执行 Assert 的 ReflectionEmit 时才能与 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 的早期版本需要使用带有 ReflectionPermission 标志的 ReflectionPermissionFlag.TypeInformation。 有关详细信息,请参阅 反射的安全注意事项。