Procedura: eseguire codice parzialmente attendibile in un oggetto sandbox

Aggiornamento: novembre 2007

Il sandboxing consiste nell'esecuzione di un'applicazione in un ambiente di sicurezza con restrizioni, per limitare le autorizzazioni di accesso al codice concesse all'applicazione. Ad esempio, i controlli scaricati in Internet Explorer vengono eseguiti utilizzando il set di autorizzazioni Internet. Le applicazioni che si trovano nelle condivisioni della rete LAN vengono eseguite nel computer utilizzando il set di autorizzazioni LocalIntranet. Per ulteriori informazioni su tali autorizzazioni, vedere Set di autorizzazioni denominati.

Il sandboxing può essere utilizzato per eseguire applicazioni parzialmente attendibili scaricate nel computer, ma anche per testare applicazioni di prossima distribuzione che verranno eseguite in attendibilità parziale in ambienti quali Intranet. Per una descrizione completa della sicurezza dall'accesso di codice (CAS) e del sandboxing, vedere Find Out What's New with Code Access Security in the .NET Framework 2.0 sul sito Web MSDN, Microsoft Developer Network (informazioni in lingua inglese).

È possibile utilizzare l'overload del metodo CreateDomain(String, Evidence, String, String, Boolean, AppDomainInitializer, array<String[]) per specificare il set di autorizzazioni per le applicazioni in esecuzione in una sandbox. L'overload consente di specificare le autorizzazioni concesse ad un'applicazione e fornire così il livello esatto di sicurezza dall'accesso di codice desiderato. Non vengono utilizzati i criteri standard di sicurezza dall'accesso di codice (non vengono applicati i criteri del computer). Se, ad esempio, l'assembly chiamato è firmato da una chiave con un nome sicuro per il quale esiste un gruppo di codice personalizzato, il gruppo di codice non viene applicato. Gli assembly caricati mediante questo overload possono disporre unicamente del set di autorizzazioni specificato oppure di un'attendibilità totale. L'assembly ottiene attendibilità totale se è inserito nella Global Assembly Cache o nell'elenco di attendibilità totale. Puntando a un assembly con attendibilità totale è tuttavia possibile ignorare l'utilizzo della sandbox.

L'overload presenta la firma riportata di seguito:

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

I parametri relativi all'overload del metodo CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, array<StrongName[]) specificano il nome dell'oggetto AppDomain, la prova fornita per l'assembly, l'oggetto AppDomainSetup che identifica la base dell'applicazione per la sandbox, il set di autorizzazioni da utilizzare e il nome sicuro per gli assembly attendibili.

La base dell'applicazione specificata nel parametro info non deve coincidere con la base dell'applicazione host. In tal caso, l'assembly ospitato potrà utilizzare il metodo Load per caricare altri assembly nella cartella, che potrebbe non essere progettata per rilevare le chiamate provenienti da chiamanti parzialmente attendibili.

Per il parametro grantSet è possibile specificare un set di autorizzazioni creato in modo esplicito oppure uno dei set di autorizzazioni denominati quali Internet o LocalIntranet. L'esempio completo fornito in questo argomento dimostra come utilizzare set di autorizzazioni denominati anziché creare un set di autorizzazioni personalizzato.

A differenza della maggior parte dei caricamenti AppDomain, la prova per l'assembly (fornita dal parametro securityInfo) non viene utilizzata per determinare il set di autorizzazioni. Quest'ultimo viene invece specificato indipendentemente dal parametro grantSet. La prova può tuttavia essere utilizzata per altri scopi, ad esempio per determinare lo spazio di memorizzazione isolato.

Per eseguire un'applicazione in una sandbox

  1. Creare il set di autorizzazioni da concedere all'applicazione.

    Nota:

    Per l'esecuzione dell'applicazione riportata nell'esempio è necessaria l'autorizzazione Execution. Per la scrittura nella console tramite tale applicazione è necessaria l'autorizzazione UIPermission. Nel codice riportato di seguito viene creato un nuovo set con tali autorizzazioni. In alternativa, è possibile utilizzare un set di autorizzazioni denominato esistente, ad esempio LocalIntranet. Per un esempio su come utilizzare un set di autorizzazioni denominato, vedere la sezione Esempio riportata più avanti in questo argomento.

    PermissionSet pset = new PermissionSet(PermissionState.None);
    pset.AddPermission(new      SecurityPermission(SecurityPermissionFlag.Execution));
    pset.AddPermission(new UIPermission(PermissionState.Unrestricted));
    
  2. Inizializzare la cartella da utilizzare come sandbox. Non utilizzare a tale scopo la cartella utilizzata dall'applicazione host. Inserendo l'applicazione in tale cartella, l'assembly host sarà in grado di caricare qualsiasi assembly al suo interno.

    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. Utilizzare l'overload del metodo CreateDomain(String, Evidence, String, String, Boolean, AppDomainInitializer, array<String[]) per creare il dominio. In questo caso la prova e il nome sicuro dell'assembly padre vengono specificati. Per il codice del metodo GetStrongName, vedere la sezione Esempio riportata più avanti in questo argomento.

    // Create the sandboxed application domain.
    AppDomain sandbox = AppDomain.CreateDomain(
    "Sandboxed Domain",
    AppDomain.CurrentDomain.Evidence,
    ads, pset, GetStrongName(Assembly.GetExecutingAssembly()));
    
  4. Eseguire l'applicazione.

    sandbox.ExecuteAssemblyByName("HelloWorld");
    

Esempio

Di seguito viene riportato un esempio completo in cui viene implementata la procedura illustrata nella sezione precedente. Nell'esempio viene illustrato come eseguire un'applicazione utilizzando le stesse autorizzazioni che questa potrebbe ottenere in un ambiente Intranet. Creare una propria applicazione di prova con cui sostituire l'assembly HelloWorld.exe riportato nell'esempio.

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;
        }

    }
}