Este artigo foi traduzido por máquina. Para visualizar o arquivo em inglês, marque a caixa de seleção Inglês. Você também pode exibir o texto Em inglês em uma janela pop-up, movendo o ponteiro do mouse sobre o texto.
Tradução
Inglês

Otimizações de segurança

Verificações de segurança podem causar problemas de desempenho para alguns aplicativos. Há duas técnicas de otimização que você pode usar para melhorar o desempenho. Uma técnica combina demandas de segurança; a outra suprime demandas de permissão para chamar códigos não gerenciados. Embora essas técnicas possam melhorar o desempenho do seu aplicativo, também podem tornar seu aplicativo vulnerável na segurança. Antes de usar essas técnicas de otimização, você deve tomar as precauções a seguir:

  • Siga o Diretrizes de codificação segura para código gerenciado.

  • Entenda as implicações de segurança das otimizações e use outros métodos para proteger seu aplicativo conforme apropriado.

  • Implementar as otimizações de segurança mínimas necessárias para melhorar o desempenho do aplicativo.

Após otimizar seu código, você deve testar o código otimizado para determinar se seu desempenho melhorou de fato. Caso contrário, você deve remover as otimizações de segurança para ajudar a evitar fraquezas inadvertidas de segurança.

Observação de cuidado Cuidado

A otimização de segurança requer que você altere a segurança de acesso a código padrão. Para evitar apresentar as vulnerabilidades de segurança em seu código, certifique-se de que você entende as implicações de segurança das técnicas de otimização antes de usar.

Para otimizar o código fazendo demandas de segurança, você pode, em algumas situações, usar uma técnica para combinar demandas.

Por exemplo, se:

  • seu código executa um número de operações em um único método, e

  • ao executar cada uma dessas operações, seu código aciona uma biblioteca de classe gerenciada que exige que seu código tenha a mesma permissão em cada chamada à biblioteca,

então:

  • você pode alterar o código para executar uma Demanda e Assert dessa permissão para reduzir a sobrecarga incorrida pelas demandas de segurança.

Se a profundidade da pilha de chamadas acima do método é grande, usar esta técnica pode resultar em um ganho de desempenho significativo.

Para ilustrar como isso funciona, suponha que o método executa operações M 100. Cada operação chamada em uma biblioteca que faz uma demanda de segurança exigindo que seu código e todos seus chamadores tenham a permissão X. Devido às demandas de segurança, cada operação faz com que o tempo de execução percorra toda pilha de chamadas examinando as permissões de cada chamador para determinar se a permissão X foi realmente concedida para cada chamador. Se a pilha de chamadas acima do método M é de profundidade de níveis n , comparações 100n são necessárias.

Para otimizar, você pode fazer o seguinte no método M:

  • A Demanda X, os quais os resultados no tempo de execução realizando um caminho de pilha (de profundidade n) para garantir que todos os chamadores certamente tenham permissão X.

  • Declare a permissão X, o que faz com que os stack walks parem no método M e substituam, reduzindo, dessa maneira, o número de comparações de permissão por 99n.

No exemplo de código seguinte, o método GetFileCreationTime tem uma representação de cadeia de caracteres de um diretório como um parâmetro e exibe o nome e a data de criação de cada arquivo neste diretório. O método estático File.GetCreationTime lê as informações dos arquivos, mas requer uma demanda e um stack walk para cada arquivo lido. O método cria uma nova instância do objeto de FileIOPermission, executa uma demanda para verificar as permissões de todos os chamadores da pilha, e declara a permissão se a demanda for bem-sucedida. Se a demanda tiver êxito, apenas um exame de pilha é executado e o método lê a hora de criação de cada arquivo no diretório passado.

using System;
using System.IO;
using System.Security;
using System.Security.Permissions;

namespace OptimizedSecurity
{
   public class FileUtil
   {
      public FileUtil()
      {
      }

      public void GetFileCreationTime(string Directory)
      {
         //Initialize DirectoryInfo object to the passed directory. 
         DirectoryInfo DirFiles = new DirectoryInfo(Directory);

         //Create a DateTime object to be initialized below.
         DateTime TheTime;

         //Get a list of files for the current directory.
         FileInfo[] Files = DirFiles.GetFiles();
         
         //Create a new instance of FileIOPermission with read 
         //permission to the current directory.
         FileIOPermission FilePermission = new FileIOPermission(FileIOPermissionAccess.Read, Directory);

         try
         {
            //Check the stack by making a demand.
            FilePermission.Demand();

            //If the demand succeeded, assert permission and 
            //perform the operation.
            FilePermission.Assert();

            for(int x = 0 ; x<= Files.Length -1 ; x++)
            {
               TheTime = File.GetCreationTime(Files[x].FullName);
               Console.WriteLine("File: {0} Created: {1:G}", Files[x].Name,TheTime );
            }
            // Revert the Assert when the operation is complete.
            CodeAccessPermission.RevertAssert();
         }
         //Catch a security exception and display an error.
         catch(SecurityException)
         {
            Console.WriteLine("You do not have permission to read this directory.");
         }                            
      }
   }
}

Se a demanda no exemplo anterior tiver êxito, cada arquivo e sua data e hora de criação são exibidas para o diretório passado. Se a demanda falhar, a exceção de segurança é interceptada e a seguinte mensagem é exibida no console:

You do not have permission to read this directory.

Uma otimização especial está disponível para o código que tem permissão para chamar o código não gerenciado. Essa otimização permite que seu código gerenciado chamar um código não gerenciado sem se sujeitar à sobrecarga de um stack walk. Declarar a permissão de código não gerenciado pode reduzir a exame de pilha, mas a otimização descrita neste tópico pode eliminá-la inteiramente. (Consulte SecurityPermission para obter mais informações sobre a permissão para chamada do código não gerenciado.)

Normalmente, uma chamada de código não gerenciado gera uma demanda de permissão de código não gerenciado, o que resulta em uma movimentação de pilha que determina se todos os chamadores terão permissão para chamar código não gerenciado. Aplique o atributo personalizado SuppressUnmanagedCodeSecurityAttribute para o método que chama em um código não gerenciado suprimindo a demanda. Este atributo substitui o stack walk completo em tempo de execução por uma verificação que verifica somente as permissões do chamador imediato em tempo de link. Na verdade, o uso deste atributo cria uma porta aberta para código não gerenciado. Somente códigos com permissão de código não gerenciado podem usar este atributo; caso contrário, ele não tem efeito.

Observação de cuidado Cuidado

Use o atributo SuppressUnmanagedCodeSecurityAttribute somente com cuidado extremo. Uso incorreto deste atributo pode criar fraquezas de segurança. O atributo SuppressUnmanagedCodeSecurityAttribute nunca deve ser usado para permitir que um código menos confiável (código que não possui uma permissão de código não gerenciada) chame um código não gerenciado.

Esse atributo é melhor aplicado somente aos pontos de entrada declarados particularmente para códigos não gerenciados, para que os códigos em outros assemblies não possam acessar e aproveitar a exclusão de segurança. Normalmente, o código gerenciado altamente confiável que usa esse atributo primeiro requer alguma permissão dos chamadores antes de chamar o código não gerenciado em favor do chamador.

O seguinte exemplo mostra o atributo SuppressUnmanagedCodeSecurityAttribute aplicado a um ponto de entrada particular.

[SuppressUnmanagedCodeSecurityAttribute()]
[DllImport("some.dll")]
private static extern void EntryPoint(string args);

Nos exemplos raros de código não gerenciado que está completamente seguro para todas as possíveis circunstâncias, um método com o atributo SuppressUnmanagedCodeSecurityAttribute pode ser exposto para outro código gerenciado diretamente tornando-o público em vez de privado. Se você escolher expor um método que tem o atributo SuppressUnmanagedCodeSecurityAttribute, a funcionalidade do código não gerenciado não deve ser apenas segura mas também impermeável ao ataque por chamadores maliciosos. Por exemplo, o código deve operar adequadamente mesmo quando os argumentos sem intenção são fabricados para especificamente fazer com que o código tenha um mau funcionamento.

As declarações e outras substituições são mais rápidas quando feitas declarativamente, enquanto as demandas são mais rápidas quando feitas imperativamente. Embora os ganhos de desempenho não podem ser dramáticos, usar substituições declarativas e demandas imperativas pode ajudar a melhorar o desempenho do código.

Contribuições da comunidade

Mostrar: