代码访问安全性和 ADO.NETCode Access Security and ADO.NET

.NET Framework 提供基于角色的安全性和代码访问安全性 (CAS),这两种安全性都可以通过公共语言运行库 (CLR) 提供的公共基础结构实现。The .NET Framework offers role-based security as well as code access security (CAS), both of which are implemented using a common infrastructure supplied by the common language runtime (CLR). 对于非托管代码,大多数应用程序都可以使用用户或主体权限执行。In the world of unmanaged code, most applications execute with the permissions of the user or principal. 因此,当拥有提升权限的用户运行恶意软件或包含错误的软件时,计算机系统可能会受到损坏并危及私有数据。As a result, computer systems can be damaged and private data compromised when malicious or error-filled software is run by a user with elevated privileges.

相对而言,.NET Framework 中执行的托管代码包括单独应用于代码的代码访问安全性。By contrast, managed code executing in the .NET Framework includes code access security, which applies to code alone. 是否允许运行代码取决于代码的来源或代码标识的其他方面,而不仅仅是主体标识。Whether the code is allowed to run or not depends on the code's origin or other aspects of the code's identity, not solely the identity of the principal. 这样可以减小滥用托管代码的可能性。This reduces the likelihood that managed code can be misused.

代码访问权限Code Access Permissions

在执行代码时,代码会提供通过 CLR 安全系统计算的证据。When code is executed, it presents evidence that is evaluated by the CLR security system. 通常,此证据由代码的来源(包括 URL、站点和区域)以及确保程序集标识的数字签名组成。Typically, this evidence comprises the origin of the code including URL, site, and zone, and digital signatures that ensure the identity of the assembly.

CLR 仅允许代码执行代码具有执行权限的那些操作。The CLR allows code to perform only those operations that the code has permission to perform. 代码可以请求权限,而这些请求需要基于管理员设置的安全策略。Code can request permissions, and those requests are honored based on the security policy set by an administrator.

备注

在 CLR 中指定的代码不能为自身授予权限。Code executing in the CLR cannot grant permissions to itself. 例如,代码可以请求并获得比安全策略允许的权限少的权限,但决不会获得比安全策略允许的权限多的权限。For example, code can request and be granted fewer permissions than a security policy allows, but it will never be granted more permissions. 在授予权限时,应该从无权限开始,然后为要执行的特定任务添加最少的权限。When granting permissions, start with no permissions at all and then add the narrowest permissions for the particular task being performed. 一开始就使用所有权限,然后拒绝各个权限会导致应用程序不安全,应用程序可能会授予不必要的权限,从而使应用程序无意中包含安全漏洞。Starting with all permissions and then denying individual ones leads to insecure applications that may contain unintentional security holes from granting more permissions than required. 有关详细信息,请参阅配置安全策略安全策略管理For more information, see Configuring Security Policy and Security Policy Management.

代码访问权限有三种类型:There are three types of code access permissions:

  • Code access permissionsCodeAccessPermission 类派生。Code access permissions derive from the CodeAccessPermission class. 需要具有权限才能访问受保护的资源(如文件和环境变量)和执行受保护的操作(如访问托管代码)。Permissions are required in order to access protected resources, such as files and environment variables, and to perform protected operations, such as accessing unmanaged code.

  • Identity permissions表示标识程序集的特征。Identity permissions represent characteristics that identify an assembly. 对程序集授予权限需要基于证据,而证据可以包括如数字签名或代码来源等项。Permissions are granted to an assembly based on evidence, which can include items such as a digital signature or where the code originated. 标识权限也从 CodeAccessPermission 基类派生。Identity permissions also derive from the CodeAccessPermission base class.

  • Role-based security permissions基于主体是否具有指定标识或是否是指定角色的成员。Role-based security permissions are based on whether a principal has a specified identity or is a member of a specified role. PrincipalPermission 类允许对活动主体进行声明性和强制性权限检查。The PrincipalPermission class allows both declarative and imperative permission checks against the active principal.

为了确定代码是否获得了访问某一资源或执行某一操作的授权,运行库的安全系统将遍历调用堆栈,将每个调用方已获得的权限与要求的权限进行比较。To determine whether code is authorized to access a resource or perform an operation, the runtime's security system traverses the call stack, comparing the granted permissions of each caller to the permission being demanded. 如果调用堆栈中的任何调用方没有要求的权限,则会引发 SecurityException 并拒绝访问。If any caller in the call stack does not have the demanded permission, a SecurityException is thrown and access is refused.

请求权限Requesting Permissions

请求权限的目的是通知运行库您的应用程序要求哪些权限才能运行,并确保应用程序只接收到实际需要的权限。The purpose of requesting permissions is to inform the runtime which permissions your application requires in order to run, and to ensure that it receives only the permissions that it actually needs. 例如,如果您的应用程序需要将数据写入本地磁盘,则需要 FileIOPermissionFor example, if your application needs to write data to the local disk, it requires FileIOPermission. 如果尚未授予该权限,则在应用程序尝试写入磁盘时将失败。If that permission hasn't been granted, the application will fail when it attempts to write to the disk. 不过,如果应用程序请求 FileIOPermission 并且尚未授予该权限,则应用程序一开始即会生成异常,因此将不会加载。However, if the application requests FileIOPermission and that permission has not been granted, the application will generate the exception at the outset and will not load.

在应用程序只需从磁盘读取数据的情况下,您可以请求永远不为应用程序授予任何写入权限。In a scenario where the application only needs to read data from the disk, you can request that it never be granted any write permissions. 在出现 Bug 或受到恶意攻击时,你的代码将不会损坏它所操作的数据。In the event of a bug or a malicious attack, your code cannot damage the data on which it operates. 有关详细信息,请参阅请求权限For more information, see Requesting Permissions.

基于角色的安全性和 CASRole-Based Security and CAS

同时实现基于角色的安全性和代码访问安全性 (CAS) 可增强应用程序的整体安全性。Implementing both role-based security and code-accessed security (CAS) enhances overall security for your application. 基于角色的安全性可以基于 Windows 帐户或自定义标识,使有关安全主体的信息可用于当前线程。Role-based security can be based on a Windows account or a custom identity, making information about the security principal available to the current thread. 此外,通常还要求应用程序基于用户提供的凭据提供对数据或资源的访问。In addition, applications are often required to provide access to data or resources based on credentials supplied by the user. 通常情况下,这种应用程序会检查用户的角色,并根据这些角色提供对资源的访问。Typically, such applications check the role of a user and provide access to resources based on those roles.

基于角色的安全性使组件能够在运行时标识当前用户及其关联的角色。Role-based security enables a component to identify current users and their associated roles at run time. 然后使用 CAS 策略映射此信息以确定运行时授予的一组权限。This information is then mapped using a CAS policy to determine the set of permissions granted at run time. 对于指定的应用程序域,主机可以更改基于角色的默认安全策略并设置表示用户的默认安全主体和与该用户关联的角色。For a specified application domain, the host can change the default role-based security policy and set a default security principal that represents a user and the roles associated with that user.

CLR 使用权限来实现其用于对托管代码实施限制的机制。The CLR uses permissions to implement its mechanism for enforcing restrictions on managed code. 基于角色的安全权限提供一种机制,用于发现用户(或代表该用户的代理)是否具有特定标识或者是否是指定角色的成员。Role-based security permissions provide a mechanism for discovering whether a user (or the agent acting on the user's behalf) has a particular identity or is a member of a specified role. 有关详细信息,请参阅安全权限For more information, see Security Permissions.

根据要生成的应用程序类型,还应考虑在数据库中实现基于角色的权限。Depending on the type of application you are building, you should also consider implementing role-based permissions in the database. 有关 SQL Server 中基于角色的安全性的详细信息,请参阅SQL Server 安全性For more information on role-based security in SQL Server, see SQL Server Security.

程序集Assemblies

程序集构成 .NET Framework 应用程序部署、版本控制、重复使用、激活范围和安全权限的基本单元。Assemblies form the fundamental unit of deployment, version control, reuse, activation scoping, and security permissions for a .NET Framework application. 程序集提供类型和资源的集合,二者结合在一起构成功能的逻辑单元。An assembly provides a collection of types and resources that are built to work together and form a logical unit of functionality. 对于 CLR,类型不存在于程序集的上下文之外。To the CLR, a type does not exist outside the context of an assembly. 有关创建和部署程序集的详细信息,请参阅用程序集编程For more information on creating and deploying assemblies, see Programming with Assemblies.

强命名程序集Strong-naming Assemblies

强名称(或数字签名)由程序集的标识组成,该标识包括程序集的简单文本名称、版本号和区域性信息(如果提供)、公钥和数字签名。A strong name, or digital signature, consists of the assembly's identity, which includes its simple text name, version number, and culture information (if provided), plus a public key and a digital signature. 数字签名使用相应私钥从程序集文件生成。The digital signature is generated from an assembly file using the corresponding private key. 程序集文件包含程序集清单,该清单包含组成程序集的所有文件的名称和哈希。The assembly file contains the assembly manifest, which contains the names and hashes of all the files that make up the assembly.

强命名程序集可为应用程序或组件提供唯一的标识,其他软件可以使用该标识显式引用应用程序或组件。Strong naming an assembly gives an application or component a unique identity that other software can use to refer explicitly to it. 强命名可以保护程序集,防止包含恶意代码的程序集冒充。Strong naming guards assemblies against being spoofed by an assembly that contains hostile code. 强命名还可以保证组件的不同版本之间的版本一致性。Strong-naming also ensures versioning consistency among different versions of a component. 对于将要部署到全局程序集缓存 (GAC) 的程序集,必须进行强命名。You must strong name assemblies that will be deployed to the Global Assembly Cache (GAC). 有关详细信息,请参阅创建和使用具有强名称的程序集For more information, see Creating and Using Strong-Named Assemblies.

ADO.NET 2.0 中的部分信任Partial Trust in ADO.NET 2.0

在 ADO.NET 2.0 中,适用于 SQL Server 的 .NET Framework 数据提供程序、适用于 OLE DB 的 .NET Framework 数据提供程序、适用于 ODBC 的 .NET Framework 数据提供程序和适用于 Oracle 的 .NET Framework 数据提供程序现在全部可以在部分信任的环境中运行。In ADO.NET 2.0, the .NET Framework Data Provider for SQL Server, the .NET Framework Data Provider for OLE DB, the .NET Framework Data Provider for ODBC, and the .NET Framework Data Provider for Oracle can now all run in partially trusted environments. 在以前版本的 .NET Framework 中,非完全信任的应用程序仅支持 System.Data.SqlClientIn previous releases of the .NET Framework, only System.Data.SqlClient was supported in less than full-trust applications.

使用 SQL Server 提供程序的部分信任应用程序必须至少具有执行和 SqlClientPermission 权限。At minimum, a partially trusted application using the SQL Server provider must have execution and SqlClientPermission permissions.

部分信任的权限属性Permission Attribute Properties for Partial Trust

对于部分信任的方案,可以使用 SqlClientPermissionAttribute 成员进一步限制 SQL Server .NET Framework 数据提供程序可以使用的功能。For partial trust scenarios, you can use SqlClientPermissionAttribute members to further restrict the capabilities available for the .NET Framework Data Provider for SQL Server.

下表列出了可用的 SqlClientPermissionAttribute 属性及其说明:The following table lists the available SqlClientPermissionAttribute properties and their descriptions:

权限属性Permission attribute property 描述Description
Action 获取或设置安全性操作。Gets or sets a security action. SecurityAttribute 继承。Inherited from SecurityAttribute.
AllowBlankPassword 启用或禁用连接字符串中空白密码。Enables or disables the use of a blank password in a connection string. 有效值为 true(启用空白密码的使用)和 false(禁用空白密码的使用)。Valid values are true (to enable the use of blank passwords) and false (to disable the use of blank passwords). DBDataPermissionAttribute 继承。Inherited from DBDataPermissionAttribute.
ConnectionString 标识允许的连接字符串。Identifies a permitted connection string. 可标识多个连接字符串。Multiple connection strings can be identified. 注意: 连接字符串中不要包含用户 ID 或密码。Note: Do not include a user ID or password in your connection string. 此版本中,不能使用 .NET Framework 配置工具更改连接字符串限制。In this release, you cannot change connection string restrictions using the .NET Framework Configuration Tool.

DBDataPermissionAttribute 继承。Inherited from DBDataPermissionAttribute.
KeyRestrictions 标识允许或不允许的连接字符串参数。Identifies connection string parameters that are allowed or disallowed. 在窗体中标识的连接字符串参数 <参数名称>=Connection string parameters are identified in the form <parameter name>=. 可指定多个参数,并用分号 (;) 进行分隔。Multiple parameters can be specified, delimited using a semicolon (;). 注意: 如果不指定 KeyRestrictions 而是将 KeyRestrictionBehavior 属性设置为 AllowOnlyPreventUsage,则不允许使用任何其他连接字符串参数。Note: If you do not specify KeyRestrictions, but you set KeyRestrictionBehavior property to AllowOnly or PreventUsage, no additional connection string parameters are allowed. DBDataPermissionAttribute 继承。Inherited from DBDataPermissionAttribute.
KeyRestrictionBehavior 将连接字符串参数标识为唯一允许的附加参数 (AllowOnly),或标识不允许的附加参数 (PreventUsage)。Identifies the connection string parameters as the only additional parameters allowed (AllowOnly), or identifies the additional parameters that are not allowed (PreventUsage). AllowOnly默认值为。AllowOnly is the default. DBDataPermissionAttribute 继承。Inherited from DBDataPermissionAttribute.
TypeID 在派生类中实现此属性时获取唯一标识符。Gets a unique identifier for this attribute when implemented in a derived class. Attribute 继承。Inherited from Attribute.
Unrestricted 表明是否声明对该资源的无限制权限。Indicates whether unrestricted permission to the resource is declared. SecurityAttribute 继承。Inherited from SecurityAttribute.

ConnectionString 语法ConnectionString Syntax

下面的示例演示如何使用配置文件的 connectionStrings 元素仅允许使用特定的连接字符串。The following example demonstrates how to use the connectionStrings element of a configuration file to allow only a specific connection string to be used. 有关从配置文件中存储和检索连接字符串的详细信息,请参阅连接字符串See Connection Strings for more information on storing and retrieving connection strings from configuration files.

<connectionStrings>  
  <add name="DatabaseConnection"   
    connectionString="Data Source=(local);Initial   
    Catalog=Northwind;Integrated Security=true;" />  
</connectionStrings>  

KeyRestrictions 语法KeyRestrictions Syntax

下面的示例启用相同的连接字符串,启用EncryptPacket Size连接字符串选项,但限制使用任何其他连接字符串选项。The following example enables the same connection string, enables the use of the Encrypt and Packet Size connection string options, but restricts the use of any other connection string options.

<connectionStrings>  
  <add name="DatabaseConnection"   
    connectionString="Data Source=(local);Initial   
    Catalog=Northwind;Integrated Security=true;"  
    KeyRestrictions="Encrypt=;Packet Size=;"  
    KeyRestrictionBehavior="AllowOnly" />  
</connectionStrings>  

包含 PreventUsage 的 KeyRestrictionBehavior 的语法KeyRestrictionBehavior with PreventUsage Syntax

下面的示例启用相同的连接字符串,并允许使用 User IdPasswordPersist Security Info 以外的所有其他连接参数。The following example enables the same connection string and allows all other connection parameters except for User Id, Password and Persist Security Info.

<connectionStrings>  
  <add name="DatabaseConnection"   
    connectionString="Data Source=(local);Initial   
    Catalog=Northwind;Integrated Security=true;"  
    KeyRestrictions="User Id=;Password=;Persist Security Info=;"  
    KeyRestrictionBehavior="PreventUsage" />  
</connectionStrings>  

包含 AllowOnly 的 KeyRestrictionBehavior 的语法KeyRestrictionBehavior with AllowOnly Syntax

以下示例启用两个连接字符串,其中还包含 Initial CatalogConnection TimeoutEncryptPacket Size 参数。The following example enables two connection strings that also contain Initial Catalog, Connection Timeout, Encrypt, and Packet Size parameters. 而所有其他的连接字符串参数都被限制。All other connection string parameters are restricted.

<connectionStrings>  
  <add name="DatabaseConnection"   
    connectionString="Data Source=(local);Initial   
    Catalog=Northwind;Integrated Security=true;"  
    KeyRestrictions="Initial Catalog;Connection Timeout=;  
       Encrypt=;Packet Size=;"   
    KeyRestrictionBehavior="AllowOnly" />  
  
  <add name="DatabaseConnection2"   
    connectionString="Data Source=SqlServer2;Initial   
    Catalog=Northwind2;Integrated Security=true;"  
    KeyRestrictions="Initial Catalog;Connection Timeout=;  
       Encrypt=;Packet Size=;"   
    KeyRestrictionBehavior="AllowOnly" />  
</connectionStrings>  

启用具有自定义权限集的部分信任Enabling Partial Trust with a Custom Permission Set

要对特定区域启用 System.Data.SqlClient 权限,系统管理员必须创建自定义的权限集,并将其设置为特定区域的权限集。To enable the use of System.Data.SqlClient permissions for a particular zone, a system administrator must create a custom permission set and set it as the permission set for a particular zone. 不能修改默认权限集(如 LocalIntranet)。Default permission sets, such as LocalIntranet, cannot be modified. 例如,若要包含System.Data.SqlClient Zone具有的LocalIntranet代码的权限,系统管理员可以为LocalIntranet复制权限集,将其重命名为 "CustomLocalIntranet",添加System.Data.SqlClient权限,导入使用caspol.exe (代码访问安全策略工具)的 CustomLocalIntranet 权限集,并将的LocalIntranet_Zone权限集设置为 CustomLocalIntranet。For example, to include System.Data.SqlClient permissions for code that has a Zone of LocalIntranet, a system administrator could copy the permission set for LocalIntranet, rename it to "CustomLocalIntranet", add the System.Data.SqlClient permissions, import the CustomLocalIntranet permission set using the Caspol.exe (Code Access Security Policy Tool), and set the permission set of LocalIntranet_Zone to CustomLocalIntranet.

示例权限集Sample Permission Set

下面是在部分受信任方案中,SQL Server .NET Framework 数据提供程序的示例权限集。The following is a sample permission set for the .NET Framework Data Provider for SQL Server in a partially trusted scenario. 有关创建自定义权限集的信息,请参阅使用 Caspol.exe 配置权限集For information on creating custom permission sets, see Configuring Permission Sets Using Caspol.exe.

<PermissionSet class="System.Security.NamedPermissionSet"  
  version="1"  
  Name="CustomLocalIntranet"  
  Description="Custom permission set given to applications on  
    the local intranet">  
  
<IPermission class="System.Data.SqlClient.SqlClientPermission, System.Data, Version=2.0.0000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"  
version="1"  
AllowBlankPassword="False">  
<add ConnectionString="Data Source=(local);Integrated Security=true;"  
 KeyRestrictions="Initial Catalog=;Connection Timeout=;  
   Encrypt=;Packet Size=;"   
 KeyRestrictionBehavior="AllowOnly" />  
 </IPermission>  
</PermissionSet>  

使用安全权限验证 ADO.NET 代码访问Verifying ADO.NET Code Access Using Security Permissions

对于部分信任方案,可以通过指定 SqlClientPermissionAttribute 来要求代码中的特定方法具有 CAS 特权。For partial-trust scenarios, you can require CAS privileges for particular methods in your code by specifying a SqlClientPermissionAttribute. 如果当前受限制的安全策略不允许该权限,在运行代码之前将引发异常。If that privilege is not allowed by the restricted security policy in effect, an exception is thrown before your code is run. 有关安全策略的详细信息,请参阅安全策略管理安全策略最佳实践For more information on security policy, see Security Policy Management and Security Policy Best Practices.

示例Example

以下示例演示如何编写要求特定连接字符串的代码。The following example demonstrates how to write code that requires a particular connection string. 该示例模拟拒绝为 System.Data.SqlClient 授予无限制权限的过程,系统管理员在实际工作中将会使用 CAS 策略实现该过程。It simulates denying unrestricted permissions to System.Data.SqlClient, which a system administrator would implement using a CAS policy in the real world.

重要

在设计 ADO.NET 的 CAS 权限时,正确的模式是以限制性最强的情况开始(无任何权限),然后添加代码执行特定任务所需的特定权限。When designing CAS permissions for ADO.NET, the correct pattern is to start with the most restrictive case (no permissions at all) and then add the specific permissions that are needed for the particular task that the code needs to perform. 相反的模式是一开始就授予所有权限,然后拒绝特定权限,这样做是不安全的,因为表达同一连接字符串可以有许多方式。The opposite pattern, starting with all permissions and then denying a specific permission, is not secure because there are many ways of expressing the same connection string. 例如,如果一开始就授予所有权限,然后尝试拒绝使用连接字符串“server=someserver”,则仍将允许使用“server=someserver.mycompany.com”。For example, if you start with all permissions and then attempt to deny the use of the connection string "server=someserver", the string "server=someserver.mycompany.com" would still be allowed. 通过在开始时始终不授予任何权限,可以降低权限集中存在漏洞的几率。By always starting by granting no permissions at all, you reduce the chances that there are holes in the permission set.

下面的代码演示 SqlClient 如何执行安全请求,如果没有相应的 CAS 权限,将引发 SecurityExceptionThe following code demonstrates how SqlClient performs the security demand, which throws a SecurityException if the appropriate CAS permissions are not in place. 控制台窗口中显示 SecurityException 输出。The SecurityException output is displayed in the console window.

using System;
using System.Data;
using System.Data.SqlClient;
using System.Security;
using System.Security.Permissions;
 
namespace PartialTrustTopic {
   public class PartialTrustHelper : MarshalByRefObject {
      public void TestConnectionOpen(string connectionString) {
         // Try to open a connection.
         using (SqlConnection connection = new SqlConnection(connectionString)) {
            connection.Open();
         }
      }
   }
 
   class Program {
      static void Main(string[] args) {
         TestCAS("Data Source=(local);Integrated Security=true", "Data Source=(local);Integrated Security=true;Initial Catalog=Test");
      }
 
      static void TestCAS(string connectString1, string connectString2) {
         // Create permission set for sandbox AppDomain.
         // This example only allows execution.
         PermissionSet permissions = new PermissionSet(PermissionState.None);
         permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
 
         // Create sandbox AppDomain with permission set that only allows execution,
         // and has no SqlClientPermissions.
         AppDomainSetup appDomainSetup = new AppDomainSetup();
         appDomainSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
         AppDomain firstDomain = AppDomain.CreateDomain("NoSqlPermissions", null, appDomainSetup, permissions);
 
         // Create helper object in sandbox AppDomain so that code can be executed in that AppDomain.
         Type helperType = typeof(PartialTrustHelper);
         PartialTrustHelper firstHelper = (PartialTrustHelper)firstDomain.CreateInstanceAndUnwrap(helperType.Assembly.FullName, helperType.FullName);
 
         try {
            // Attempt to open a connection in the sandbox AppDomain.
            // This is expected to fail.
            firstHelper.TestConnectionOpen(connectString1);
            Console.WriteLine("Connection opened, unexpected.");
         }
         catch (System.Security.SecurityException ex) {
            Console.WriteLine("Failed, as expected: {0}",
                ex.FirstPermissionThatFailed);
 
            // Uncomment the following line to see Exception details.
            // Console.WriteLine("BaseException: " + ex.GetBaseException());
         }
 
         // Add permission for a specific connection string.
         SqlClientPermission sqlPermission = new SqlClientPermission(PermissionState.None);
         sqlPermission.Add(connectString1, "", KeyRestrictionBehavior.AllowOnly);
 
         permissions.AddPermission(sqlPermission);
 
         AppDomain secondDomain = AppDomain.CreateDomain("OneSqlPermission", null, appDomainSetup, permissions);
         PartialTrustHelper secondHelper = (PartialTrustHelper)secondDomain.CreateInstanceAndUnwrap(helperType.Assembly.FullName, helperType.FullName);
 
         // Try connection open again, it should succeed now.
         try {
            secondHelper.TestConnectionOpen(connectString1);
            Console.WriteLine("Connection opened, as expected.");
         }
         catch (System.Security.SecurityException ex) {
            Console.WriteLine("Unexpected failure: {0}", ex.Message);
         }
 
         // Try a different connection string. This should fail.
         try {
            secondHelper.TestConnectionOpen(connectString2);
            Console.WriteLine("Connection opened, unexpected.");
         }
         catch (System.Security.SecurityException ex) {
            Console.WriteLine("Failed, as expected: {0}", ex.Message);
         }
      }
   }
}
Imports System.Data
Imports System.Data.SqlClient
Imports System.Security
Imports System.Security.Permissions

Namespace PartialTrustTopic
   Public Class PartialTrustHelper
      Inherits MarshalByRefObject
      Public Sub TestConnectionOpen(ByVal connectionString As String)
         ' Try to open a connection.
         Using connection As New SqlConnection(connectionString)
            connection.Open()
         End Using
      End Sub
   End Class

   Class Program
      Public Shared Sub Main(ByVal args As String())
         TestCAS("Data Source=(local);Integrated Security=true", "Data Source=(local);Integrated Security=true;Initial Catalog=Test")
      End Sub

      Public Shared Sub TestCAS(ByVal connectString1 As String, ByVal connectString2 As String)
         ' Create permission set for sandbox AppDomain.
         ' This example only allows execution.
         Dim permissions As New PermissionSet(PermissionState.None)
         permissions.AddPermission(New SecurityPermission(SecurityPermissionFlag.Execution))

         ' Create sandbox AppDomain with permission set that only allows execution,
         ' and has no SqlClientPermissions.
         Dim appDomainSetup As New AppDomainSetup()
         appDomainSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase
         Dim firstDomain As AppDomain = AppDomain.CreateDomain("NoSqlPermissions", Nothing, appDomainSetup, permissions)

         ' Create helper object in sandbox AppDomain so that code can be executed in that AppDomain.
         Dim helperType As Type = GetType(PartialTrustHelper)
         Dim firstHelper As PartialTrustHelper = DirectCast(firstDomain.CreateInstanceAndUnwrap(helperType.Assembly.FullName, helperType.FullName), PartialTrustHelper)

         Try
            ' Attempt to open a connection in the sandbox AppDomain.
            ' This is expected to fail.
            firstHelper.TestConnectionOpen(connectString1)
            Console.WriteLine("Connection opened, unexpected.")
         Catch ex As System.Security.SecurityException

            ' Uncomment the following line to see Exception details.
            ' Console.WriteLine("BaseException: " + ex.GetBaseException());
            Console.WriteLine("Failed, as expected: {0}", ex.FirstPermissionThatFailed)
         End Try

         ' Add permission for a specific connection string.
         Dim sqlPermission As New SqlClientPermission(PermissionState.None)
         sqlPermission.Add(connectString1, "", KeyRestrictionBehavior.AllowOnly)

         permissions.AddPermission(sqlPermission)

         Dim secondDomain As AppDomain = AppDomain.CreateDomain("OneSqlPermission", Nothing, appDomainSetup, permissions)
         Dim secondHelper As PartialTrustHelper = DirectCast(secondDomain.CreateInstanceAndUnwrap(helperType.Assembly.FullName, helperType.FullName), PartialTrustHelper)

         ' Try connection open again, it should succeed now.
         Try
            secondHelper.TestConnectionOpen(connectString1)
            Console.WriteLine("Connection opened, as expected.")
         Catch ex As System.Security.SecurityException
            Console.WriteLine("Unexpected failure: {0}", ex.Message)
         End Try

         ' Try a different connection string. This should fail.
         Try
            secondHelper.TestConnectionOpen(connectString2)
            Console.WriteLine("Connection opened, unexpected.")
         Catch ex As System.Security.SecurityException
            Console.WriteLine("Failed, as expected: {0}", ex.Message)
         End Try
      End Sub
   End Class
End Namespace

在控制台窗口中应看到以下输出:You should see this output in the Console window:

Failed, as expected: <IPermission class="System.Data.SqlClient.  
SqlClientPermission, System.Data, Version=2.0.0.0,   
  Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1"  
  AllowBlankPassword="False">  
<add ConnectionString="Data Source=(local);Initial Catalog=  
  Northwind;Integrated Security=SSPI" KeyRestrictions=""  
KeyRestrictionBehavior="AllowOnly"/>  
</IPermission>  
  
Connection opened, as expected.  
Failed, as expected: Request failed.  

与非托管代码的互操作性Interoperability with Unmanaged Code

在 CLR 外部运行的代码称为非托管代码。Code that runs outside the CLR is called unmanaged code. 因此,安全机制(如 CAS)不能应用于非托管代码。Therefore, security mechanisms such as CAS cannot be applied to unmanaged code. COM 组件、ActiveX 接口和 Windows API 函数都是非托管代码的示例。COM components, ActiveX interfaces, and Windows API functions are examples of unmanaged code. 在执行非托管代码时应考虑特殊安全注意事项,以便不会危害应用程序的整体安全性。Special security considerations apply when executing unmanaged code so that you do not jeopardize overall application security. 有关详细信息,请参阅与非托管代码交互操作For more information, see Interoperating with Unmanaged Code.

.NET Framework 可以通过 COM 互操作提供访问,因此还支持与现有 COM 组件的向后兼容。The .NET Framework also supports backward compatibility to existing COM components by providing access through COM interop. 通过使用 COM 互操作工具导入相关的 COM 类型,可以将 COM 组件合并到 .NET Framework 应用程序中。You can incorporate COM components into a .NET Framework application by using COM interop tools to import the relevant COM types. 一旦导入后,就可以使用 COM 类型了。Once imported, the COM types are ready to use. 通过将程序集元数据导出到类型库并将托管组件注册为 COM 组件,COM 互操作还可以使 COM 客户端访问托管代码。COM interop also enables COM clients to access managed code by exporting assembly metadata to a type library and registering the managed component as a COM component. 有关详细信息,请参阅高级 COM 互操作性For more information, see Advanced COM Interoperability.

请参阅See also