反射提供获取有关类型和成员的信息以及访问成员(即调用方法和构造函数、获取和设置属性值、添加和删除事件处理程序等)的功能。 使用反射来获取有关类型和成员的信息不受限制。 所有代码都可以使用反射来执行以下任务:
- 枚举类型和成员,并检查其元数据。
- 枚举并检查程序集和模块。
与之相反,使用反射来访问成员会受到限制。 从 .NET Framework 4 开始,只有受信任的代码可以使用反射来访问安全关键成员。 此外,只有受信任的代码可以使用反射来访问无法直接访问已编译代码的非公共成员。 最后,使用反射访问安全关键成员的代码必须具有安全关键成员所需的任何权限,就像使用已编译的代码一样。
根据必要的权限,代码可以使用反射来执行以下类型的访问:
访问不是安全关键的公共成员。
若这些成员不是安全关键,则访问可进入编译代码的非公共成员。 此类非公共成员的示例包括:
调用代码的基类的受保护成员。 (在反射中,这称为系列级访问权限。)
调用代码的程序集中的
internal
成员(Visual Basic 中的Friend
成员)。 (在反射中,这称为程序集级别的访问。)包含调用代码的类的其他实例的私有成员。
例如,沙盒应用程序域中运行的代码仅限于此列表中所述的访问权限,除非应用程序域授予其他权限。
从 .NET Framework 2.0 Service Pack 1 开始,尝试访问通常无法访问的成员将生成目标对象授权集的需求以及带 ReflectionPermission 标志的 ReflectionPermissionFlag.MemberAccess。 完全信任运行的代码(例如,从命令行启动的应用程序中的代码)始终可以满足这些权限。 (如本文后续部分所述,访问安全关键成员时会受到限制。)
沙盒应用程序域可以向 ReflectionPermission 授予 ReflectionPermissionFlag.MemberAccess 标志,如本文后续部分中的访问通常不可访问的成员中所述。
访问安全关键成员
如果成员具备 SecurityCriticalAttribute,或者属于具有 SecurityCriticalAttribute 的类型,或者位于安全关键程序集,则该成员为安全关键型。 从 .NET Framework 4 开始,访问安全关键成员的规则如下所示:
透明代码无法使用反射来访问安全关键成员,即使代码完全受信任也是如此。 引发一个 MethodAccessException、FieldAccessException 或 TypeAccessException。
使用部分信任运行的代码被视为透明。
无论安全关键成员是直接通过编译的代码访问还是通过使用反射访问,这些规则都是相同的。
从命令行运行的应用程序代码将以“完全信任”运行。 只要它未标记为透明,它就可以使用反射来访问安全关键成员。 当使用部分信任(例如,在沙盒应用程序域中)运行相同的代码时,程序集的信任级别确定它是否可以访问安全关键代码:如果程序集具有强名称且安装在全局程序集缓存中,则它是受信任的程序集,并且可以调用安全关键成员。 如果不受信任,即便它未被标记为透明,仍会变得透明,并且无法访问安全关键成员。
反射和透明度
从 .NET Framework 4 开始,公共语言运行时从多个因素(包括程序集的信任级别和应用程序域的信任级别)确定类型或成员的透明度级别。 反射提供IsSecurityCritical和IsSecuritySafeCriticalIsSecurityTransparent属性,使你能够发现类型的透明度级别。 下表显示了这些属性的有效组合。
安全级别 | IsSecurityCritical | IsSecuritySafeCritical | IsSecurityTransparent |
---|---|---|---|
危急 | true |
false |
false |
安全-关键 | true |
true |
false |
透明 | false |
false |
true |
使用这些属性比检查程序集及其类型的安全注释、检查当前信任级别以及尝试复制运行时规则要简单得多。 例如,当从命令行中运行时,相同的类型可以是安全关键,或者在沙盒应用程序域中运行时,它们又是安全-透明的。
在MethodBase、FieldInfo、TypeBuilder、MethodBuilder和DynamicMethod类上有类似的属性。 (对于其他反射和反射发出抽象化,安全属性应用到关联的方法;例如,在它们应用于属性访问器的属性的情况下)。
访问通常无法访问的成员
若要使用反射来调用根据公共语言运行时的辅助功能规则无法访问的成员,必须向代码授予以下两个权限之一:
若要允许代码调用任何非公共成员:必须授予代码ReflectionPermission,并使用ReflectionPermissionFlag.MemberAccess标志。
注释
默认情况下,安全策略拒绝对源自 Internet 的代码的此权限。 不应向源自 Internet 的代码授予此权限。
若要允许代码调用任何非公共成员,只要包含已调用成员的程序集的授予集与包含调用代码的程序集的授予集相同或为其子集:您的代码必须被授予具有ReflectionPermission标志的ReflectionPermissionFlag.RestrictedMemberAccess。
例如,假设你为应用程序域授予 Internet 权限以及带 ReflectionPermission 标志的 ReflectionPermissionFlag.RestrictedMemberAccess,则使用两个程序集 A 和 B 运行 Internet 应用程序。
程序集 A 可以使用反射来访问程序集 B 的私有成员,因为程序集 B 的授予集不包括尚未授予 A 的任何权限。
程序集 A 不能使用反射来访问 .NET Framework 程序集的私有成员(如 mscorlib.dll),因为 mscorlib.dll 是完全受信任的,因此有尚未被授予给程序集 A 的权限。代码访问安全性在运行时审核堆栈将引发 MemberAccessException。
序列化
对于序列化,带 SecurityPermission 标志的 SecurityPermissionAttribute.SerializationFormatter,无论其访问级别是什么,都能够获取和设置序列化类型的成员。 此权限使代码能够发现和更改实例的专用状态。 (除被授予适当权限以外,在元数据中该类型必须标记为可序列化。)
类型 MethodInfo 的参数
避免编写接收MethodInfo 参数的公共组件,尤其是对于受信任的代码。 此类成员可能更容易受到恶意代码的攻击。 例如,考虑采用 MethodInfo 参数的高度受信任代码中的公共成员。 假定公共成员通过提供的参数间接调用Invoke方法。 如果公共成员不执行必要的权限检查,则对 Invoke 方法的调用将始终成功,因为安全系统确定调用方高度受信任。 即使恶意代码没有直接调用方法的权限,它仍可以通过调用公共成员间接执行此作。
版本信息
从 .NET Framework 4 开始,透明代码无法使用反射来访问安全关键成员。
该 ReflectionPermissionFlag.RestrictedMemberAccess 标志在 .NET Framework 2.0 Service Pack 1 中引入。 早期版本的 .NET Framework 需要使用反射访问非公共成员的代码的 ReflectionPermissionFlag.MemberAccess 标志。 这是绝对不会授予给部分受信任的代码的权限。
从 .NET Framework 2.0 开始,使用反射获取有关非公共类型和成员的信息不需要任何权限。 在早期版本中,ReflectionPermission 需要与 ReflectionPermissionFlag.TypeInformation 标志一起使用。