如何:在沙盒中运行部分信任的代码How to: Run Partially Trusted Code in a Sandbox

注意

代码访问安全性和部分受信任的代码Code Access Security and Partially Trusted Code

.NET Framework 提供一种机制,对在相同应用程序中运行的不同代码强制实施不同的信任级别,该机制称为代码访问安全性 (CAS)。The .NET Framework provides a mechanism for the enforcement of varying levels of trust on different code running in the same application called Code Access Security (CAS). .NET Framework 中的代码访问安全性不应用作基于代码来源或其他标识方面强制实施安全边界的一种机制。Code Access Security in .NET Framework should not be used as a mechanism for enforcing security boundaries based on code origination or other identity aspects. 我们正在更新相应指南以反映代码访问安全性,并且将不支持把安全透明代码用作部分受信任的代码(尤其是未知来源的代码)的安全边界。We are updating our guidance to reflect that Code Access Security and Security-Transparent Code will not be supported as a security boundary with partially trusted code, especially code of unknown origin. 建议在未实施其他安全措施的情况下,不要加载和执行未知来源的代码。We advise against loading and executing code of unknown origins without putting alternative security measures in place.

此策略适用于 .NET Framework 的所有版本,但不适用于 Silverlight 中所含的 .NET Framework。This policy applies to all versions of .NET Framework, but does not apply to the .NET Framework included in Silverlight.

沙盒处理是在受限制的安全环境中运行代码的做法,从而限制授予代码的访问权限。Sandboxing is the practice of running code in a restricted security environment, which limits the access permissions granted to the code. 例如,如果你有来自不完全信任的源的托管库,则不应将其作为完全受信任运行。For example, if you have a managed library from a source you do not completely trust, you should not run it as fully trusted. 相反,应将代码放在将权限限制为预计需要的权限(例如 Execution 权限)的沙盒中。Instead, you should place the code in a sandbox that limits its permissions to those that you expect it to need (for example, Execution permission).

沙盒处理还可用于测试将在部分受信任环境中运行的分发的代码。You can also use sandboxing to test code you will be distributing that will run in partially trusted environments.

AppDomain 是为托管应用程序提供沙盒的有效途径。An AppDomain is an effective way of providing a sandbox for managed applications. 用于运行部分受信任的代码的应用程序域具有定义受保护资源的权限,这些资源在于 AppDomain 内运行时可用。Application domains that are used for running partially trusted code have permissions that define the protected resources that are available when running within that AppDomain. AppDomain 内运行的代码由与 AppDomain 关联的权限约束并且仅能访问指定的资源。Code that runs inside the AppDomain is bound by the permissions associated with the AppDomain and is allowed to access only the specified resources. AppDomain 还包括用于标识程序集(作为完全受信任的程序集加载)的 StrongName 数组。The AppDomain also includes a StrongName array that is used to identify assemblies that are to be loaded as fully trusted. 这使 AppDomain 的创建者能启动新沙盒域,该域允许特定的帮助程序程序集为完全受信任的程序集。This enables the creator of an AppDomain to start a new sandboxed domain that allows specific helper assemblies to be fully trusted. 将程序集加载为完全受信任的程序集的另一种方法是:将其放在全局程序集缓存中;但是,这会在该计算机上创建的所有应用程序域中将程序集加载为完全受信任的程序集。Another option for loading assemblies as fully trusted is to place them in the global assembly cache; however, that will load assemblies as fully trusted in all application domains created on that computer. 强名称列表支持提供更多限制性决定的每个 AppDomain 的决策。The list of strong names supports a per-AppDomain decision that provides more restrictive determination.

可以使用 AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) 方法重载为在沙盒中运行的应用程序指定权限集。You can use the AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) method overload to specify the permission set for applications that run in a sandbox. 此重载使你能够指定所需代码访问安全性的确切级别。This overload enables you to specify the exact level of code access security you want. 使用此重载加载到 AppDomain 中的程序集可以仅具有指定的授予集,也可以是完全受信任的程序集。Assemblies that are loaded into an AppDomain by using this overload can either have the specified grant set only, or can be fully trusted. 如果程序集位于全局程序集缓存中或在 fullTrustAssembliesStrongName)数组参数中列出,则该程序集被授予完全信任。The assembly is granted full trust if it is in the global assembly cache or listed in the fullTrustAssemblies (the StrongName) array parameter. 只应将已知完全受信任的程序集添加到 fullTrustAssemblies 列表中。Only assemblies known to be fully trusted should be added to the fullTrustAssemblies list.

重载具有以下签名:The overload has the following signature:

AppDomain.CreateDomain( string friendlyName,  
                        Evidence securityInfo,  
                        AppDomainSetup info,  
                        PermissionSet grantSet,  
                        params StrongName[] fullTrustAssemblies);  

CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) 方法重载的参数指定 AppDomain 的名称、AppDomain 的证据、标识沙盒的应用程序基的 AppDomainSetup 对象、要使用的权限集和完全受信任的程序集的强名称。The parameters for the CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) method overload specify the name of the AppDomain, the evidence for the AppDomain, the AppDomainSetup object that identifies the application base for the sandbox, the permission set to use, and the strong names for fully trusted assemblies.

出于安全原因,在 info 参数中指定的应用程序基不应为主机应用程序的应用程序基。For security reasons, the application base specified in the info parameter should not be the application base for the hosting application.

对于 grantSet 参数,可以指定已显式创建的权限集或由 GetStandardSandbox 方法创建的标准权限集。For the grantSet parameter, you can specify either a permission set you have explicitly created, or a standard permission set created by the GetStandardSandbox method.

与大多数 AppDomain 加载不同,AppDomain 的证据(由 securityInfo 参数提供)不用于确定部分受信任的程序集的授予集。Unlike most AppDomain loads, the evidence for the AppDomain (which is provided by the securityInfo parameter) is not used to determine the grant set for the partially trusted assemblies. 相反,它由 grantSet 参数单独指定。Instead, it is independently specified by the grantSet parameter. 但证据可以用于其它目的,例如确定独立存储范围。However, the evidence can be used for other purposes such as determining the isolated storage scope.

在沙盒中运行应用程序To run an application in a sandbox

  1. 创建要授予给不受信任的应用程序的权限集。Create the permission set to be granted to the untrusted application. 可以授予的最小权限是 Execution 权限。The minimum permission you can grant is Execution permission. 还可以授予其它你认为可能对不受信任的代码安全的权限;例如,IsolatedStorageFilePermissionYou can also grant additional permissions you think might be safe for untrusted code; for example, IsolatedStorageFilePermission. 下面的代码创建仅具有 Execution 权限的新权限集。The following code creates a new permission set with only Execution permission.

    PermissionSet permSet = new PermissionSet(PermissionState.None);  
    permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));  
    

    或者,可以使用现有的已命名权限集,如 Internet。Alternatively, you can use an existing named permission set, such as Internet.

    Evidence ev = new Evidence();  
    ev.AddHostEvidence(new Zone(SecurityZone.Internet));  
    PermissionSet internetPS = SecurityManager.GetStandardSandbox(ev);  
    

    GetStandardSandbox 方法返回 Internet 权限集或 LocalIntranet 权限集,具体取决于证据中的区域。The GetStandardSandbox method returns either an Internet permission set or a LocalIntranet permission set depending on the zone in the evidence. GetStandardSandbox 还为一些作为引用传递的证据对象构造标识权限。GetStandardSandbox also constructs identity permissions for some of the evidence objects passed as references.

  2. 为包含调用不受信任的代码的宿主类(在此示例中,命名为 Sandboxer)的程序集签名。Sign the assembly that contains the hosting class (named Sandboxer in this example) that calls the untrusted code. 将用于对程序集签名的 StrongName 添加到 CreateDomain 调用的 fullTrustAssemblies 参数的 StrongName 数组中。Add the StrongName used to sign the assembly to the StrongName array of the fullTrustAssemblies parameter of the CreateDomain call. 主机类必须作为完全受信任的类运行,以启用部分信任代码的执行或为部分信任应用程序提供服务。The hosting class must run as fully trusted to enable the execution of the partial-trust code or to offer services to the partial-trust application. 这就是读取程序集的 StrongName 的方式:This is how you read the StrongName of an assembly:

    StrongName fullTrustAssembly = typeof(Sandboxer).Assembly.Evidence.GetHostEvidence<StrongName>();  
    

    不需要将 .NET framework 程序集(如 mscorlib 和 System.dll)添加到完全信任列表中,因为它们是作为完全受信任的程序集从全局程序集缓存中加载的。.NET Framework assemblies such as mscorlib and System.dll do not have to be added to the full-trust list because they are loaded as fully trusted from the global assembly cache.

  3. 初始化 CreateDomain 方法的 AppDomainSetup 参数。Initialize the AppDomainSetup parameter of the CreateDomain method. 使用此参数,可以控制新的 AppDomain 的许多设置。With this parameter, you can control many of the settings of the new AppDomain. ApplicationBase 属性是一个重要设置,并且应不同于宿主应用程序的 AppDomainApplicationBase 属性。The ApplicationBase property is an important setting, and should be different from the ApplicationBase property for the AppDomain of the hosting application. 如果 ApplicationBase 设置相同,则部分信任应用程序可以获取主机应用程序以加载(作为完全受信任的应用程序)它定义的异常,从而攻击它。If the ApplicationBase settings are the same, the partial-trust application can get the hosting application to load (as fully trusted) an exception it defines, thus exploiting it. 这是为什么不建议使用 catch(异常)的另一个原因。This is another reason why a catch (exception) is not recommended. 将主机的应用程序基设置为与沙盒应用程序的应用程序基不同,这可降低攻击风险。Setting the application base of the host differently from the application base of the sandboxed application mitigates the risk of exploits.

    AppDomainSetup adSetup = new AppDomainSetup();  
    adSetup.ApplicationBase = Path.GetFullPath(pathToUntrusted);  
    
  4. 使用已指定的参数调用 CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) 方法重载来创建应用程序域。Call the CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) method overload to create the application domain using the parameters we have specified.

    此方法的签名是:The signature for this method is:

    public static AppDomain CreateDomain(string friendlyName,   
        Evidence securityInfo, AppDomainSetup info, PermissionSet grantSet,   
        params StrongName[] fullTrustAssemblies)  
    

    其他信息:Additional information:

    • 这是唯一将 PermissionSet 作为参数的 CreateDomain 方法的重载,因而它是唯一允许以部分信任设置加载应用程序的重载。This is the only overload of the CreateDomain method that takes a PermissionSet as a parameter, and thus the only overload that lets you load an application in a partial-trust setting.

    • evidence 参数不用于计算权限集;它由 .NET Framework 的其它功能用于标识。The evidence parameter is not used to calculate a permission set; it is used for identification by other features of the .NET Framework.

    • 此重载必须设置info 参数的 ApplicationBase 属性。Setting the ApplicationBase property of the info parameter is mandatory for this overload.

    • fullTrustAssemblies 参数具有 params 关键字,这意味着不需要创建 StrongName 数组。The fullTrustAssemblies parameter has the params keyword, which means that it is not necessary to create a StrongName array. 允许将 0、1 或更多强名称作为参数传递。Passing 0, 1, or more strong names as parameters is allowed.

    • 要用于创建应用程序域的代码是:The code to create the application domain is:

    AppDomain newDomain = AppDomain.CreateDomain("Sandbox", null, adSetup, permSet, fullTrustAssembly);  
    
  5. 将代码加载到已创建的沙盒 AppDomain 中。Load the code into the sandboxing AppDomain that you created. 有两种方法可以做到这一点:This can be done in two ways:

    第二种方法非常可取,因为这样可以更轻松地将参数传递给新的 AppDomain 实例。The second method is preferable, because it makes it easier to pass parameters to the new AppDomain instance. CreateInstanceFrom 方法提供两大重要功能:The CreateInstanceFrom method provides two important features:

    • 可以使用指向不包含你的程序集的位置的基本代码。You can use a code base that points to a location that does not contain your assembly.

    • 可以在完全信任 (PermissionState.Unrestricted) 的 Assert 下进行创建,这样就可以创建关键类的一个实例。You can do the creation under an Assert for full-trust (PermissionState.Unrestricted), which enables you to create an instance of a critical class. (每当你的程序集没有透明度标记并作为完全受信任的程序集加载时就会发生这种情况。)因此,必须谨慎地使用此函数只创建你信任的代码,建议在新的应用程序域中只创建完全受信任的类的实例。(This happens whenever your assembly has no transparency markings and is loaded as fully trusted.) Therefore, you have to be careful to create only code that you trust with this function, and we recommend that you create only instances of fully trusted classes in the new application domain.

    ObjectHandle handle = Activator.CreateInstanceFrom(  
    newDomain, typeof(Sandboxer).Assembly.ManifestModule.FullyQualifiedName,  
           typeof(Sandboxer).FullName );  
    

    请注意,若要创建新域中的类的实例,该类必须扩展 MarshalByRefObjectNote that in order to create an instance of a class in a new domain, the class has to extend the MarshalByRefObject class

    class Sandboxer:MarshalByRefObject  
    
  6. 将新的域实例解包到此域中的引用中。Unwrap the new domain instance into a reference in this domain. 此引用用于执行不受信任的代码。This reference is used to execute the untrusted code.

    Sandboxer newDomainInstance = (Sandboxer) handle.Unwrap();  
    
  7. 调用你刚刚创建的 Sandboxer 类的实例中的 ExecuteUntrustedCode 方法。Call the ExecuteUntrustedCode method in the instance of the Sandboxer class you just created.

    newDomainInstance.ExecuteUntrustedCode(untrustedAssembly, untrustedClass, entryPoint, parameters);  
    

    此调用在具有受限权限的沙盒应用程序域中执行。This call is executed in the sandboxed application domain, which has restricted permissions.

    public void ExecuteUntrustedCode(string assemblyName, string typeName, string entryPoint, Object[] parameters)  
    {  
        //Load the MethodInfo for a method in the new assembly. This might be a method you know, or   
        //you can use Assembly.EntryPoint to get to the entry point in an executable.  
        MethodInfo target = Assembly.Load(assemblyName).GetType(typeName).GetMethod(entryPoint);  
        try  
        {  
            // Invoke the method.  
            target.Invoke(null, parameters);  
        }  
        catch (Exception ex)  
        {  
        //When information is obtained from a SecurityException extra information is provided if it is   
        //accessed in full-trust.  
            new PermissionSet(PermissionState.Unrestricted).Assert();  
            Console.WriteLine("SecurityException caught:\n{0}", ex.ToString());  
            CodeAccessPermission.RevertAssert();  
            Console.ReadLine();  
        }  
    }  
    

    System.Reflection 用于获取部分受信任的程序集中的方法的句柄。System.Reflection is used to get a handle of a method in the partially trusted assembly. 该句柄可以用于以安全的方式、使用最小权限来执行代码。The handle can be used to execute code in a safe way with minimum permissions.

    在前面的代码中,请在打印 SecurityException 之前备注完全信任权限的 AssertIn the previous code, note the Assert for the full-trust permission before printing the SecurityException.

    new PermissionSet(PermissionState.Unrestricted).Assert()  
    

    完全信任断言用于从 SecurityException 中获取扩展信息。The full-trust assert is used to obtain extended information from the SecurityException. 没有 Assert,则 SecurityExceptionToString 方法将发现堆栈上有部分受信任的代码,并将限制返回的信息。Without the Assert, the ToString method of SecurityException will discover that there is partially trusted code on the stack and will restrict the information returned. 如果部分信任代码可以读取该信息,可能会导致安全问题,但可以通过不授予 UIPermission 来缓解此风险。This could cause security issues if the partial-trust code could read that information, but the risk is mitigated by not granting UIPermission. 完全信任断言应慎用,且仅在确定不允许部分信任代码提升为完全信任代码时使用。The full-trust assert should be used sparingly and only when you are sure that you are not allowing partial-trust code to elevate to full trust. 一般来说,不要调用同一函数中不信任的代码,也不要在调用完全信任断言后调用代码。As a rule, do not call code you do not trust in the same function and after you called an assert for full trust. 始终在完成使用断言后还原断言,这是个好做法。It is good practice to always revert the assert when you have finished using it.

示例Example

下面的示例实现前一部分中的过程。The following example implements the procedure in the previous section. 在此示例中,Visual Studio 解决方案中一个名为 Sandboxer 的项目还包含一个名为 UntrustedCode 的项目(该项目实现类 UntrustedClass)。In the example, a project named Sandboxer in a Visual Studio solution also contains a project named UntrustedCode, which implements the class UntrustedClass. 此方案假定你已下载库程序集,该程序集包含应该返回 truefalse 的方法,以指示你提供的数字是否为斐波纳契数。This scenario assumes that you have downloaded a library assembly containing a method that is expected to return true or false to indicate whether the number you provided is a Fibonacci number. 相反,该方法会尝试从你的计算机中读取文件。Instead, the method attempts to read a file from your computer. 下面的示例演示不受信任的代码。The following example shows the untrusted code.

using System;  
using System.IO;  
namespace UntrustedCode  
{  
    public class UntrustedClass  
    {  
        // Pretend to be a method checking if a number is a Fibonacci  
        // but which actually attempts to read a file.  
        public static bool IsFibonacci(int number)  
        {  
           File.ReadAllText("C:\\Temp\\file.txt");  
           return false;  
        }  
    }  
}  

下面的示例演示执行不受信任的代码的 Sandboxer 应用程序代码。The following example shows the Sandboxer application code that executes the untrusted code.

using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.IO;  
using System.Security;  
using System.Security.Policy;  
using System.Security.Permissions;  
using System.Reflection;  
using System.Runtime.Remoting;  
  
//The Sandboxer class needs to derive from MarshalByRefObject so that we can create it in another   
// AppDomain and refer to it from the default AppDomain.  
class Sandboxer : MarshalByRefObject  
{  
    const string pathToUntrusted = @"..\..\..\UntrustedCode\bin\Debug";  
    const string untrustedAssembly = "UntrustedCode";  
    const string untrustedClass = "UntrustedCode.UntrustedClass";  
    const string entryPoint = "IsFibonacci";  
    private static Object[] parameters = { 45 };  
    static void Main()  
    {  
        //Setting the AppDomainSetup. It is very important to set the ApplicationBase to a folder   
        //other than the one in which the sandboxer resides.  
        AppDomainSetup adSetup = new AppDomainSetup();  
        adSetup.ApplicationBase = Path.GetFullPath(pathToUntrusted);  
  
        //Setting the permissions for the AppDomain. We give the permission to execute and to   
        //read/discover the location where the untrusted code is loaded.  
        PermissionSet permSet = new PermissionSet(PermissionState.None);  
        permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));  
  
        //We want the sandboxer assembly's strong name, so that we can add it to the full trust list.  
        StrongName fullTrustAssembly = typeof(Sandboxer).Assembly.Evidence.GetHostEvidence<StrongName>();  
  
        //Now we have everything we need to create the AppDomain, so let's create it.  
        AppDomain newDomain = AppDomain.CreateDomain("Sandbox", null, adSetup, permSet, fullTrustAssembly);  
  
        //Use CreateInstanceFrom to load an instance of the Sandboxer class into the  
        //new AppDomain.   
        ObjectHandle handle = Activator.CreateInstanceFrom(  
            newDomain, typeof(Sandboxer).Assembly.ManifestModule.FullyQualifiedName,  
            typeof(Sandboxer).FullName  
            );  
        //Unwrap the new domain instance into a reference in this domain and use it to execute the   
        //untrusted code.  
        Sandboxer newDomainInstance = (Sandboxer) handle.Unwrap();  
        newDomainInstance.ExecuteUntrustedCode(untrustedAssembly, untrustedClass, entryPoint, parameters);  
    }  
    public void ExecuteUntrustedCode(string assemblyName, string typeName, string entryPoint, Object[] parameters)  
    {  
        //Load the MethodInfo for a method in the new Assembly. This might be a method you know, or   
        //you can use Assembly.EntryPoint to get to the main function in an executable.  
        MethodInfo target = Assembly.Load(assemblyName).GetType(typeName).GetMethod(entryPoint);  
        try  
        {  
            //Now invoke the method.  
            bool retVal = (bool)target.Invoke(null, parameters);  
        }  
        catch (Exception ex)  
        {  
            // When we print informations from a SecurityException extra information can be printed if we are   
            //calling it with a full-trust stack.  
            new PermissionSet(PermissionState.Unrestricted).Assert();  
            Console.WriteLine("SecurityException caught:\n{0}", ex.ToString());  
            CodeAccessPermission.RevertAssert();  
            Console.ReadLine();  
        }  
    }  
}  

请参阅See also