Como executar código parcialmente confiável em uma área restrita

 

System_CAPS_cautionCuidado

Segurança de Acesso do Código e Código Parcialmente Confiável

O .NET Framework fornece um mecanismo para a imposição de níveis variáveis de confiança em códigos diferentes em execução no mesmo aplicativo chamado CAS (Segurança de Acesso do Código).  O CAS no .NET Framework não deve ser usado como um mecanismo de imposição de limites de segurança com base na origem do código ou em outros aspectos da identidade. Estamos atualizando nossas diretrizes para refletir que o CAS e o Código Transparente de Segurança não terão suporte como um limite de segurança com código parcialmente confiável, especialmente o código de origem desconhecida. Não aconselhamos carregar e executar códigos de origens desconhecidas sem a adoção de medidas de segurança alternativas no local.

Essa política é aplicável à todas as versões do .NET Framework, mas não é aplicável ao .NET Framework incluído no Silverlight.

Modo seguro é a prática de execução de código em um ambiente de segurança restrito, o que limita as permissões de acesso concedidas ao código. Por exemplo, se você tiver uma biblioteca gerenciada de uma fonte que não confiável completamente, você não deve executá-lo como totalmente confiável. Em vez disso, você deve colocar o código em uma área de segurança que limita suas permissões para aqueles que você espera que ele precisa (por exemplo, Execution permissão).

Você também pode usar o modo seguro para testar código você distribuirá que serão executados em ambientes parcialmente confiáveis.

Um AppDomain é uma maneira eficaz de fornecer uma área restrita para aplicativos gerenciados. Domínios de aplicativo que são usados para executar código parcialmente confiável tem permissões que definem os recursos protegidos que estão disponíveis quando em execução que AppDomain. Código que é executado dentro do AppDomain vinculado, as permissões associadas a AppDomain e tem permissão para acessar somente os recursos especificados. O AppDomain também inclui um StrongName matriz que é usado para identificar os assemblies devem ser carregados como totalmente confiável. Isso permite que o criador de um AppDomain para iniciar um novo domínio em modo seguro que permite que os assemblies específicos auxiliar ser totalmente confiável. Outra opção para carregar assemblies totalmente confiáveis é colocá-los no cache de assembly global; No entanto, que irá carregar assemblies como totalmente confiável em todos os domínios de aplicativo criados no computador. Nomes de lista de alta segurança suporta uma por-AppDomain decisão que fornece determinação mais restritiva.

Você pode usar o AppDomain.CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) sobrecarga de método para especificar a permissão definida para aplicativos que são executados em uma área restrita. Essa sobrecarga permite que você especifique o nível exato de segurança de acesso do código desejado. Assemblies são carregados em um AppDomain usando essa sobrecarga pode ter especificado conceder somente conjunto, ou pode ser totalmente confiável. O assembly é concedido confiança total, se ele estiver no cache de assembly global ou listados no fullTrustAssemblies (o StrongName) parâmetro de matriz. Apenas os assemblies conhecidos por serem totalmente confiáveis devem ser adicionados para o fullTrustAssemblies lista.

A sobrecarga tem a seguinte assinatura:

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

Os parâmetros para o CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) sobrecarga do método de especificar o nome do AppDomain, a evidência para o AppDomain, o AppDomainSetup objeto que identifica o aplicativo de base para a área restrita, o conjunto de permissões para usar e os nomes fortes para assemblies totalmente confiáveis.

Por motivos de segurança, a base do aplicativo especificado no info parâmetro não deve ser a base para o aplicativo de hospedagem de aplicativo.

Para o grantSet parâmetro, você pode especificar um conjunto de permissões que você criou explicitamente ou uma permissão padrão conjunto criado pelo GetStandardSandbox método.

Ao contrário da maioria AppDomain carrega as evidências para o AppDomain (que é fornecido pelo securityInfo parâmetro) não é usado para determinar a concessão definido para os assemblies parcialmente confiáveis. Em vez disso, ela é especificada por independentemente do grantSet parâmetro. No entanto, a evidência pode ser usada para outras finalidades, como determinar o escopo do armazenamento isolado.

Para executar um aplicativo em uma área restrita

  1. Crie conjunto de permissões para ser concedido ao aplicativo não confiável. A permissão mínima que você pode conceder é Execution permissão. Você também pode conceder permissões adicionais que você acha que talvez seja seguras para código não confiável. Por exemplo, IsolatedStorageFilePermission. O código a seguir cria um novo conjunto de permissões com apenas Execution permissão.

    PermissionSet permSet = new PermissionSet(PermissionState.None);
    permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
    

    Como alternativa, você pode usar um conjunto de permissões nomeadas existente, como a Internet.

    Evidence ev = new Evidence();
    ev.AddHostEvidence(new Zone(SecurityZone.Internet));
    PermissionSet internetPS = SecurityManager.GetStandardSandbox(ev);
    

    O GetStandardSandbox método retorna um Internet conjunto de permissões ou um LocalIntranet permissão definida dependendo da zona em que a evidência.GetStandardSandbox também constrói as permissões de identidade para alguns dos objetos de evidência passados como referências.

  2. Assinar o assembly que contém a classe de hospedagem (chamada Sandboxer neste exemplo) que chama o código não confiável. Adicionar o StrongName usado para assinar o assembly a StrongName matriz do fullTrustAssemblies parâmetro o CreateDomain chamar. A classe de hospedagem deve ser executado como totalmente confiável para habilitar a execução do código parcialmente confiável ou para oferecer serviços para o aplicativo de confiança parcial. Isso é como você ler o StrongName de um assembly:

    StrongName fullTrustAssembly = typeof(Sandboxer).Assembly.Evidence.GetHostEvidence<StrongName>();
    

    Assemblies do .NET framework, como mscorlib e System. dll não precisa ser adicionado à lista de confiança total porque eles são carregados como totalmente confiáveis do cache de assembly global.

  3. Inicializar o AppDomainSetup parâmetro o CreateDomain método. Com esse parâmetro, você pode controlar muitas das configurações do novo AppDomain. O ApplicationBase propriedade é uma configuração importante e deve ser diferente do ApplicationBase propriedade para o AppDomain do aplicativo host. Se o ApplicationBase configurações são as mesmas, o aplicativo de confiança parcial pode obter carregar (como totalmente confiável) uma exceção define, explorando-a, portanto, o aplicativo de hospedagem. Esse é outro motivo por que não é recomendado um catch (exception). Configurando o aplicativo base do host de forma diferente da base do aplicativo do aplicativo em área restrita minimiza o risco de explorações.

    AppDomainSetup adSetup = new AppDomainSetup();
    adSetup.ApplicationBase = Path.GetFullPath(pathToUntrusted);
    

  4. Chamar o CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) sobrecarga de método para criar o domínio de aplicativo usando os parâmetros que especificamos.

    A assinatura para este método é:

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

    Informações adicionais:

    • Essa é a única sobrecarga de CreateDomain método que usa um PermissionSet como um parâmetro e, portanto, a única sobrecarga que permite que você carregam um aplicativo em um ambiente de confiança parcial.

    • O evidence parâmetro não é usado para calcular um conjunto de permissões; ela é usada para identificação por outros recursos do .NET Framework.

    • Definindo o ApplicationBase propriedade o info parâmetro é obrigatório para essa sobrecarga.

    • O fullTrustAssemblies parâmetro tem o params palavra-chave, o que significa que não é necessário criar um StrongName matriz. É permitido passar 0, 1 ou mais nomes fortes como parâmetros.

    • O código para criar o domínio de aplicativo é:

    AppDomain newDomain = AppDomain.CreateDomain("Sandbox", null, adSetup, permSet, fullTrustAssembly);
    
  5. Carregar o código para o modo seguro AppDomain que você criou. Isso pode ser feito de duas maneiras:

    O segundo método é preferível, pois ele torna mais fácil passar parâmetros para o novo AppDomain instância. OCreateInstanceFrom método fornece dois recursos importantes:

    • Você pode usar uma base de código que aponta para um local que não contém o assembly.

    • Você pode fazer a criação em um Assert para confiança total (PermissionState.Unrestricted), que permite que você crie uma instância de uma classe crítica. (Isso ocorre sempre que o assembly não tem marcas nenhuma transparência e é carregado como totalmente confiável). Portanto, você precisa ter cuidado para criar somente código confiável com essa função, e é recomendável que você crie apenas instâncias de classes totalmente confiáveis no novo domínio de aplicativo.

    ObjectHandle handle = Activator.CreateInstanceFrom(
    newDomain, typeof(Sandboxer).Assembly.ManifestModule.FullyQualifiedName,
           typeof(Sandboxer).FullName );
    

    Observe que, para criar uma instância de uma classe em um novo domínio, a classe tem que estender o MarshalByRefObject classe

    class Sandboxer:MarshalByRefObject
    
  6. Decodificar a nova instância de domínio em uma referência nesse domínio. Essa referência é usada para executar o código não confiável.

    Sandboxer newDomainInstance = (Sandboxer) handle.Unwrap();
    
  7. Chamar o ExecuteUntrustedCode método na instância do Sandboxer classe que você acabou de criar.

    newDomainInstance.ExecuteUntrustedCode(untrustedAssembly, untrustedClass, entryPoint, parameters);
    

    Essa chamada é executada no domínio de aplicativo em área restrita, que possui permissões restritas.

    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 é usado para obter um identificador de um método no assembly parcialmente confiável. O identificador pode ser usado para executar código de forma segura com permissões mínimas.

    No código anterior, observe o Assert para a permissão de confiança total antes de imprimir o SecurityException.

    new PermissionSet(PermissionState.Unrestricted)).Assert()
    

    A declaração de confiança total é usada para obter informações estendidas do SecurityException. Sem o Assert, o ToString método SecurityException descobrirá que há código parcialmente confiável na pilha e restringirá as informações retornadas. Isso pode causar problemas de segurança se o código de confiança parcial pode ler essas informações, mas o risco é atenuado concedendo não UIPermission. A declaração de confiança total deve ser usada com moderação e somente quando você tiver certeza de que você não permitem o código de confiança parcial para elevar à confiança total. Como regra, não chame código que não confiável na mesma função e depois chamado uma declaração de confiança total. É recomendável sempre reverter a asserção quando terminar de usá-lo.

Exemplo

O exemplo a seguir implementa o procedimento na seção anterior. No exemplo, um projeto chamado Sandboxer em um Visual Studio solução também contém um projeto chamado UntrustedCode, que implementa a classe UntrustedClass. Este cenário pressupõe que você baixou um assembly de biblioteca que contém um método que deve retornar true ou false para indicar se o número fornecido é um número Fibonacci. Em vez disso, o método tenta ler um arquivo do seu computador. O exemplo a seguir mostra o código não confiável.

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

A exemplo a seguir mostra o Sandboxer código de aplicativo que executa o código não confiável.

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

Mostrar: