如何:运行沙盒中部分受信任的代码

更新:2007 年 11 月

沙盒是在受限的安全环境中运行应用程序的一种做法,这种做法是要限制授予应用程序的代码访问权限。例如,下载到 Internet Explorer 的控件使用 Internet 权限集运行。驻留在局域网共享上的应用程序在计算机上使用 LocalIntranet 权限集运行。(有关这些权限的更多信息,请参见命名的权限集。)

可以使用沙盒运行下载到计算机上的部分受信任的应用程序。也可以使用沙盒测试将分发的、将在部分受信任的环境(例如 Intranet)中运行的应用程序。有关代码访问安全性 (CAS) 和沙盒的完整说明,请参见 Microsoft Developer Network (MSDN) 上的 Find Out What's New with Code Access Security in the .NET Framework 2.0(了解 .NET Framework 2.0 中代码访问安全性的新增功能)。

可以使用 CreateDomain(String, Evidence, String, String, Boolean, AppDomainInitializer, array<String[]) 方法重载为沙盒中运行的应用程序指定权限集。利用此重载可以指定向应用程序授予的权限,从而获得所需的确切代码访问安全性级别。它不利用标准 CAS 策略(不适用计算机策略)。例如,如果调用的程序集使用强名称密钥签名,且该强名称具有自定义代码组,则将不应用该代码组。使用此重载加载的程序集只能具有指定的授予集,否则必须具有完全信任。 如果程序集位于全局程序集缓存或完全信任列表中,则会向其授予完全信任。但是,如果目标为完全信任的程序集,则将不使用沙盒。

该重载具有以下签名:

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

CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) 方法重载的各个参数分别指定 AppDomain 的名称、程序集的证据、标识沙盒的应用程序基的 AppDomainSetup 对象、要使用的权限集以及受信任程序集的强名称。

info 参数中指定的应用程序基不应为宿主应用程序的应用程序基。否则,寄宿的程序集将能够使用 Load 方法将其他程序集加载到该文件夹中,这样可能无法检测到部分受信任的调用方发出的调用。

对于 grantSet 参数,您可以指定显式创建的权限集,也可以指定某个命名的权限集(例如 Internet 或 LocalIntranet)。本主题中提供的完整示例演示如何使用命名的权限集而不是创建自定义的权限集。

与大多数 AppDomain 加载不同,此处授予集并不使用程序集的证据(由 securityInfo 参数提供)确定,而是单独由 grantSet 参数指定。但是,该证据也可以用于其他目的,例如确定独立存储。

运行沙盒中的应用程序

  1. 创建要向应用程序授予的权限集。

    说明:

    此示例中的应用程序需要 Execution 权限才能运行,并需要 UIPermission 才能向控制台写入数据。下面的代码将利用这些权限创建一个新的权限集。另外,您也可以使用现有的命名权限集,例如 LocalIntranet。有关如何使用命名的权限集的示例,请参见本主题后面的“示例”一节。

    PermissionSet pset = new PermissionSet(PermissionState.None);
    pset.AddPermission(new      SecurityPermission(SecurityPermissionFlag.Execution));
    pset.AddPermission(new UIPermission(PermissionState.Unrestricted));
    
  2. 初始化将用作沙盒的文件夹。请勿使用宿主应用程序正在使用的文件夹。如果将应用程序放在宿主文件夹中,宿主程序集将能够加载该文件夹中的所有程序集。

    AppDomainSetup ads = new AppDomainSetup();
    // Identify the folder to use for the sandbox.
    ads.ApplicationBase = "C:\\Sandbox";
    // Copy the application you want to run to the sandbox. File.Copy("HelloWorld.exe","C:\\sandbox\\HelloWorld.exe",true);
    
  3. 使用 CreateDomain(String, Evidence, String, String, Boolean, AppDomainInitializer, array<String[]) 方法重载创建域。在本示例中,已指定父程序集的证据和强名称。有关 GetStrongName 方法的代码,请参见本主题后面的“示例”一节。

    // Create the sandboxed application domain.
    AppDomain sandbox = AppDomain.CreateDomain(
    "Sandboxed Domain",
    AppDomain.CurrentDomain.Evidence,
    ads, pset, GetStrongName(Assembly.GetExecutingAssembly()));
    
  4. 运行该应用程序。

    sandbox.ExecuteAssemblyByName("HelloWorld");
    

示例

下面的示例是实现上一节中所述过程的完整示例。此示例演示如何使用在 Intranet 环境中授予的权限运行应用程序。您需要创建自己的测试应用程序来替换示例中的 HelloWorld.exe 程序集。

Imports System
Imports System.Collections
Imports System.Diagnostics
Imports System.Security
Imports System.Security.Permissions
Imports System.Security.Policy
Imports System.Reflection
Imports System.IO



Class Program

    Shared Sub Main(ByVal args() As String)
        ' Create the permission set to grant to other assemblies.
        ' In this case we are granting the permissions found in the LocalIntranet zone.
        Dim pset As PermissionSet = GetNamedPermissionSet("LocalIntranet")
        If pset Is Nothing Then
            Return
        End If
        ' Optionally you can create your own permission set by explicitly adding permissions.
        '     PermissionSet pset = new PermissionSet(PermissionState.None);
        '     pset.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
        '     pset.AddPermission(new UIPermission(PermissionState.Unrestricted));
        Dim ads As New AppDomainSetup()
        ' Identify the folder to use for the sandbox.
        ads.ApplicationBase = "C:\Sandbox"
        ' Copy the application to be executed to the sandbox.
        File.Copy("HelloWorld.exe", "C:\sandbox\HelloWorld.exe", True)

        Dim hostEvidence As New Evidence()
        ' Commenting out the following two statements has no effect on the sample.
        ' The grant set is determined by the grantSet parameter, not the evidence 
        ' for the assembly.  However, the evidence can be used for other reasons, 
        ' for example, isolated storage.
        hostEvidence.AddHost(New Zone(SecurityZone.Intranet))
        hostEvidence.AddHost(New Url("C:\Sandbox"))

        ' Create the sandboxed domain.
        Dim sandbox As AppDomain = AppDomain.CreateDomain("Sandboxed Domain", hostEvidence, ads, pset, GetStrongName([Assembly].GetExecutingAssembly()))
        sandbox.ExecuteAssemblyByName("HelloWorld")

    End Sub 'Main


    '' <summary>
    '' Get a strong name that matches the specified assembly.
    '' </summary>
    '' <exception cref="ArgumentNullException">
    '' if <paramref name="assembly"/> is null
    '' </exception>
    '' <exception cref="InvalidOperationException">
    '' if <paramref name="assembly"/> does not represent a strongly named assembly
    '' </exception>
    '' <param name="assembly">Assembly to create a StrongName for</param>
    '' <returns>A StrongName for the given assembly</returns>
    '' 
    Public Shared Function GetStrongName(ByVal [assembly] As [Assembly]) As StrongName
        If [assembly] Is Nothing Then
            Throw New ArgumentNullException("assembly")
        End If
        Dim assemblyName As AssemblyName = [assembly].GetName()
        Debug.Assert(Not (assemblyName Is Nothing), "Could not get assembly name")

        ' Get the public key blob.
        Dim publicKey As Byte() = assemblyName.GetPublicKey()
        If publicKey Is Nothing OrElse publicKey.Length = 0 Then
            Throw New InvalidOperationException("Assembly is not strongly named")
        End If
        Dim keyBlob As New StrongNamePublicKeyBlob(publicKey)

        ' Return the strong name.
        Return New StrongName(keyBlob, assemblyName.Name, assemblyName.Version)

    End Function 'GetStrongName

    Private Shared Function GetNamedPermissionSet(ByVal name As String) As PermissionSet
        Dim policyEnumerator As IEnumerator = SecurityManager.PolicyHierarchy()

        ' Move through the policy levels to the machine policy level.
        While policyEnumerator.MoveNext()
            Dim currentLevel As PolicyLevel = CType(policyEnumerator.Current, PolicyLevel)

            If currentLevel.Label = "Machine" Then
                Dim copy As NamedPermissionSet = currentLevel.GetNamedPermissionSet(name)
                Return CType(copy, PermissionSet)
            End If
        End While
        Return Nothing

    End Function 'GetNamedPermissionSet
End Class 'Program 
using System;
using System.Collections;
using System.Diagnostics;
using System.Security;
using System.Security.Permissions;
using System.Security.Policy;
using System.Reflection;
using System.IO;

namespace SimpleSandboxing
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create the permission set to grant to other assemblies.
            // In this case we are granting the permissions found in the LocalIntranet zone.
            PermissionSet pset = GetNamedPermissionSet("LocalIntranet");
            if (pset == null)
                return;
            // Optionally you can create your own permission set by explicitly adding permissions.
            //     PermissionSet pset = new PermissionSet(PermissionState.None);
            //     pset.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
            //     pset.AddPermission(new UIPermission(PermissionState.Unrestricted));
            AppDomainSetup ads = new AppDomainSetup();
            // Identify the folder to use for the sandbox.
            ads.ApplicationBase = "C:\\Sandbox";
            // Copy the application to be executed to the sandbox.
            File.Copy("HelloWorld.exe", "C:\\sandbox\\HelloWorld.exe", true);

            Evidence hostEvidence = new Evidence();
            // Commenting out the following two statements has no effect on the sample.
            // The grant set is determined by the grantSet parameter, not the evidence 
            // for the assembly.  However, the evidence can be used for other reasons, 
            // for example, isolated storage.
            hostEvidence.AddHost(new Zone(SecurityZone.Intranet));
            hostEvidence.AddHost(new Url("C:\\Sandbox"));

            // Create the sandboxed domain.
            AppDomain sandbox = AppDomain.CreateDomain(
                "Sandboxed Domain",
                hostEvidence,
                ads,
                pset,
                GetStrongName(Assembly.GetExecutingAssembly()));
            sandbox.ExecuteAssemblyByName("HelloWorld");
        }

        /// <summary>
        /// Get a strong name that matches the specified assembly.
        /// </summary>
        /// <exception cref="ArgumentNullException">
        /// if <paramref name="assembly"/> is null
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// if <paramref name="assembly"/> does not represent a strongly named assembly
        /// </exception>
        /// <param name="assembly">Assembly to create a StrongName for</param>
        /// <returns>A StrongName for the given assembly</returns>
        /// 
        public static StrongName GetStrongName(Assembly assembly)
        {
            if (assembly == null)
                throw new ArgumentNullException("assembly");

            AssemblyName assemblyName = assembly.GetName();
            Debug.Assert(assemblyName != null, "Could not get assembly name");

            // Get the public key blob.
            byte[] publicKey = assemblyName.GetPublicKey();
            if (publicKey == null || publicKey.Length == 0)
                throw new InvalidOperationException("Assembly is not strongly named");

            StrongNamePublicKeyBlob keyBlob = new StrongNamePublicKeyBlob(publicKey);

            // Return the strong name.
            return new StrongName(keyBlob, assemblyName.Name, assemblyName.Version);
        }
        private static PermissionSet GetNamedPermissionSet(string name)
        {
            IEnumerator policyEnumerator = SecurityManager.PolicyHierarchy();

            // Move through the policy levels to the machine policy level.
            while (policyEnumerator.MoveNext())
            {
                PolicyLevel currentLevel = (PolicyLevel)policyEnumerator.Current;

                if (currentLevel.Label == "Machine")
                {
                    NamedPermissionSet copy = currentLevel.GetNamedPermissionSet(name);
                    return (PermissionSet)copy;
                }
            }
            return null;
        }

    }
}