Практическое руководство. Выполнение не вполне безопасного кода в изолированной среде

Обновлен: Ноябрь 2007

Выполнение кода в изолированной среде — это способ запуска приложений в защищенной среде, ограничивающей разрешения на доступ к коду, предоставленные приложению. Например, элементы управления, загружаемые в Internet Explorer, выполняются с использованием набора разрешений Internet. Приложения, размещенные на общих сетевых ресурсах в локальной сети, выполняются на компьютере с использованием набора разрешений LocalIntranet. (Дополнительные сведения об этих разрешениях см. в разделе Именованные наборы разрешений.)

Выполнение в изолированной среде можно использовать для запуска не вполне безопасных приложений, загруженных на компьютер. Выполнение в изолированной среде можно также использовать для тестирования приложений, которые будут распространяться для работы с неполным уровнем доверия в таких средах, как интрасеть. Полное описание механизма управления доступом для кода и выполнения в изолированной среде см. по адресу Найти в сети MSDN.

Перегрузку метода CreateDomain(String, Evidence, String, String, Boolean, AppDomainInitializer, array<String[]) также можно использовать для задания набора разрешений для приложений, выполняемых в изолированной среде. Эта перегрузка позволяет определять разрешения, предоставляемые приложению и обеспечивающие необходимый уровень управления доступом для кода. Она не использует стандартную политику управления доступом для кода (политика компьютера не применяется). Например, если вызываемая сборка подписана ключом со строгим именем, для которого существует пользовательская группа кода, группа кода применяться не будет. Сборки, загружаемые посредством этой перегрузки, могут иметь либо указанный набор предоставленных разрешений, либо полное доверие. Сборке предоставляется полное доверие, если она находится в глобальном кэше сборок или в списке полного доверия. Однако сборка с полным доступом выполняется не в изолированной среде.

Перегрузка имеет следующую подпись:

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. Создайте набор разрешений, который будет предоставлен приложению.

    Bb763046.alert_note(ru-ru,VS.90).gifПримечание.

    Приложению из этого примера требуется разрешение 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;
        }

    }
}