Практическое руководство. Выполнение не вполне безопасного кода в изолированной средеHow to: Run Partially Trusted Code in a Sandbox

Внимание!

Управление доступом для кода и частично доверенный кодCode Access Security and Partially Trusted Code

Платформа .NET Framework предоставляет механизм для принудительного применения различных уровней доверия к разным частям кода, выполняемым в одном и том же приложении. Этот механизм называется управлением доступом для кода.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, кроме платформы .NET Framework в составе Silverlight.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. Сборке предоставляется полное доверие, если она находится в глобальном кэше сборок или указана в параметре массива fullTrustAssemblies (StrongName).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. Кроме того, можно предоставить дополнительные разрешения, которые, по вашему мнению, будут безопасными для ненадежного кода, например разрешение IsolatedStorageFilePermission.You 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, используемый для подписания сборки, в массив StrongName в параметре fullTrustAssemblies вызова метода CreateDomain.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. Инициализируйте параметр AppDomainSetup метода CreateDomain.Initialize the AppDomainSetup parameter of the CreateDomain method. С помощью этого параметра можно управлять многими настройками нового домена AppDomain.With this parameter, you can control many of the settings of the new AppDomain. Свойство ApplicationBase является важной настройкой и должно отличаться от свойства ApplicationBase домена AppDomain основного приложения.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. Это еще одна причина, по которой не рекомендуется выполнять перехват исключения.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:

    • Это единственная перегрузка метода CreateDomain, принимающая в качестве параметра набор разрешений PermissionSet, то есть единственная перегрузка, позволяющая загружать приложение в режиме частичного доверия.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.

    • Задание свойства ApplicationBase параметра info является обязательным для этой перегрузки.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. Разрешается передавать в качестве параметров ноль, одно или несколько строгих имен.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.

    • Во-вторых, при работе в режиме полного доверия (Assert) можно использовать для создания экземпляра критически важного класса метод PermissionState.Unrestricted.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 );  
    

    Обратите внимание, что для создания экземпляра класса в новом домене класс должен расширять класс MarshalByRefObject.Note 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. Вызовите метод ExecuteUntrustedCode в созданном экземпляре класса Sandboxer.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.

    В предыдущем коде обратите внимание на метод Assert для получения разрешения с полным доверием перед печатью SecurityException.In 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, метод ToString исключения SecurityException обнаружит наличие в стеке кода с частичным доверием и ограничит возвращенные сведения.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. В этом примере проект с именем Sandboxer в решении Visual Studio содержит проект с именем 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. В этом сценарии предполагается, что загружена библиотечная сборка, содержащая метод, который должен вернуть значение true или false, указывающее, является ли предоставленное число числом Фибоначчи.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