Share via


HOW TO:在沙箱中執行部分信任的程式碼

更新:2007 年 11 月

沙箱作業是指在限制的安全性環境中執行應用程式,這可限制授與應用程式的程式碼存取使用權限。例如,下載到 Internet Explorer 的控制項便使用 Internet 使用權限集合執行。位於區域網路共用的應用程式則在電腦上使用 LocalIntranet 使用權限集合執行(如需這些使用權限的詳細資訊,請參閱具名使用權限集合)。

您可以使用沙箱作業執行下載到電腦上的部分信任應用程式。您也可以使用沙箱作業測試將在內部網路等環境中散發並以部分信任權限執行的應用程式。如需程式碼存取安全性 (CAS) 和沙箱作業的完整描述,請參閱 MSDN (Microsoft Developer Network) 上的了解 (英文)。

您可以使用 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 參數指定的。不過,辨識項可使用於其他用途,例如判斷隔離儲存區 (Isolated Storage)。

若要在沙箱中執行應用程式

  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");
    

範例

下列範例是實作上一節中程序的完整範例。這個範例會顯示如何使用一般在內部網路環境中所授與的相同使用權限集合,來執行應用程式。您必須建立自己的測試應用程式來取代範例中的 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;
        }

    }
}