使用 Deny 方法

更新:2007 年 11 月

调用 Deny 会防止访问由拒绝的权限指定的资源。如果您的代码调用 Deny,下游调用方随后请求拒绝的权限,则即使所有调用方都有访问该资源的权限,安全检查也会失败。要使 Deny 生效,请求的权限和拒绝的权限不必完全匹配,请求的权限也不必为拒绝的权限的子集。但是,如果两个权限的交集为空(即二者没有共有内容),则调用 Deny 无效。请注意,Deny 不能重写调用堆栈上执行 Assert 的较深的代码。如果调用堆栈上较深的代码执行 Assert,则该较深代码可以访问调用堆栈上较高级别的代码所拒绝的资源。

可以在您的代码中使用对 Deny 的调用来避免使自己承担责任,因为 Deny 使得您的代码不可能被用来访问被拒绝的资源。但是,对 Deny 的调用不会阻止下游调用方将来的安全断言。

下图说明在使用 Deny 时会发生什么情况。假定下列有关程序集 A、B、C、D 和 E 及权限 P1 的语句为真:

  • P1 表示读取 C 驱动器上所有文件的权限。

  • 已将 P1 授予程序集 A、B、C、D 和 E。

  • 方法 F 发出对权限 P1 的要求。

  • 方法 C 创建 P1 类的一个实例,然后调用 P1 的 Deny 方法。

  • 方法 A 包含在程序集 A 中,方法 B 包含在程序集 B 中,依此类推。

使用 Deny

权限要求和拒绝

方法 C 中对 Deny 的调用可影响对 P1 的要求的结果。例如,假定方法 A 调用 B,B 调用 C,C 调用 E,E 调用 F。因为方法 F 直接访问 P1 保护的资源,所以方法 F 通过调用 P1 的 Demand 方法(或者使用声明式要求)来调用安全检查,检查是否有 P1。此要求导致运行库检查调用堆栈中所有调用方的权限(从程序集 E 开始)。因为程序集 E 已被授予 P1 权限,运行库会接着检查程序集 C 的权限。但是因为方法 C 已拒绝 P1,所以方法 E 调用的安全检查会在此处失败,并引发 SecurityException。程序集 C 及其调用方(程序集 A 和 B)是否已被授予 P1 并不重要;安全检查仍会失败。因为方法 C 调用 Deny,所以程序集 A 和 B 中的代码无法访问 P1 保护的资源。

下面的代码说明使用 Deny 方法重写安全检查的声明式语法。在本示例中,ReflectionPermission 语法指定两个值:一个 SecurityAction 枚举和 TypeInformation 属性的设置。TypeInformation 设置为真以指定此权限代表通过反射查看私有成员的权限,而传递 SecurityAction.Deny 可以拒绝该权限。有关可以指定的值的完整列表,请参见 ReflectionPermission 的说明。通过此安全声明,该方法不能通过反射读取类型的私有成员。

Option Strict
Option Explicit
Imports System
Imports System.Security.Permissions
<ReflectionPermissionAttribute(SecurityAction.Deny, TypeInformation = true ")> Public Class 
MyClass1
   Public Sub New()
   End Sub
   Public Sub GetPublicMembers ()
      ' Access public members through reflection.
   End Sub
End Class
using System;
using System.Security.Permissions;

[ReflectionPermissionAttribute(SecurityAction.Deny, TypeInformation = true)]
public class MyClass
{
   public MyClass()
   {    
   }   

   public void GetPublicMembers()
   {
      //Access public members through reflection.
   }  
}

下面的代码说明使用 Deny 方法重写安全检查的强制性语法。在本例中,声明了 ReflectionPermission 对象并向其构造函数传递了 ReflectionPermissionFlag.TypeInformation 以初始化当前权限。调用 Deny 方法时,决不能使用代码和调用方通过反射来读取私有字段。

Option Explicit
Option Strict
Imports System
Imports System.Security.Permissions
Public Class MyClass1
   Public Sub New()
   End Sub
   Public Sub ReadRegistry()
      Dim MyPermission As New ReflectionPermission (ReflectionPermissionFlag.TypeInformation)
      MyPermission.Deny()
      ' Access public members through reflection.
   End Sub 
End Class
using System;
using System.Security.Permissions;

public class MyClass {
   public MyClass() {    
   }   

   public void ReadRegistry() { 
      ReflectionPermission MyPermission = new ReflectionPermission (ReflectionPermissionFlag.TypeInformation);
      MyPermission.Deny();

      // Access public members through reflection.
   }  
}

使用 Deny 的规范问题

当拒绝 FileIOPermissionRegistryPermissionWebPermissionUrlIdentityPermissionSiteIdentityPermissionEnvironmentPermission 时,您应该非常小心,这是因为单个文件、注册表项、URL 和系统路径都可以使用多个名称进行描述。例如,可以通过多种方式引用单个文件 MyFile.log,包括“c:\MyFile.log”和“\\MyMachineName\c$\MyFile.log”。如果您创建一个权限表示对“c:\MyFile.log”的访问权限,然后对代码拒绝该权限,则您的代码可能仍可以使用替换路径“\\MyMachineName\c$\MyFile.log”访问该文件。

您可以使用 PermitOnlyDeny 的组合来避免规范化问题。PermitOnly 只允许您指定资源的多个可能名称中的一个,其副作用是即使该资源使用任何其他名称,它也将拒绝访问该资源。在使用 PermitOnly 指定资源的一个允许的名称之后,您应该使用 Deny 禁止访问使用该名称的资源。

以下代码结合使用 DenyPermitOnly 防止您的代码访问名为 MyLog.log 的资源。此代码还阻止使用所有替换名称或路径来访问资源。

<FileIOPermissionAttribute(SecurityAction.PermitOnly, All := "C:\ "), FileIOPermissionAttribute(SecurityAction.Deny, All := "C:\MyLog.log")>
[FileIOPermissionAttribute(SecurityAction.PermitOnly, All = @"C:\ ")]
[FileIOPermissionAttribute(SecurityAction.Deny, All = @"C:\MyLog.log")] 

请参见

概念

重写安全检查

参考

SecurityAction

RegistryPermissionAttribute

其他资源

利用属性扩展元数据

代码访问安全性