Partilhar via


Cenários sem reconhecimento de DI para Proteção de Dados no ASP.NET Core

Por Rick Anderson

O sistema ASP.NET Core Data Protection é normalmente adicionado a um contêiner de serviço e consumido por componentes dependentes por meio de injeção de dependência (DI). No entanto, há casos em que isso não é viável ou desejado, especialmente ao importar o sistema para um aplicativo existente.

Para dar suporte a esses cenários, o pacote Microsoft.AspNetCore.DataProtection.Extensions fornece um tipo concreto, , que oferece uma maneira simples de usar a Proteção de Dados sem depender da DI. O tipo DataProtectionProvider implementa IDataProtectionProvider. A construção de DataProtectionProvider requer apenas o fornecimento de uma instância DirectoryInfo para indicar onde as chaves criptográficas do provedor devem ser armazenadas, como visto no exemplo de código a seguir:

using System;
using System.IO;
using Microsoft.AspNetCore.DataProtection;

public class Program
{
    public static void Main(string[] args)
    {
        // Get the path to %LOCALAPPDATA%\myapp-keys
        var destFolder = Path.Combine(
            System.Environment.GetEnvironmentVariable("LOCALAPPDATA"),
            "myapp-keys");

        // Instantiate the data protection system at this folder
        var dataProtectionProvider = DataProtectionProvider.Create(
            new DirectoryInfo(destFolder));

        var protector = dataProtectionProvider.CreateProtector("Program.No-DI");
        Console.Write("Enter input: ");
        var input = Console.ReadLine();

        // Protect the payload
        var protectedPayload = protector.Protect(input);
        Console.WriteLine($"Protect returned: {protectedPayload}");

        // Unprotect the payload
        var unprotectedPayload = protector.Unprotect(protectedPayload);
        Console.WriteLine($"Unprotect returned: {unprotectedPayload}");

        Console.WriteLine();
        Console.WriteLine("Press any key...");
        Console.ReadKey();
    }
}

/*
 * SAMPLE OUTPUT
 *
 * Enter input: Hello world!
 * Protect returned: CfDJ8FWbAn6...ch3hAPm1NJA
 * Unprotect returned: Hello world!
 *
 * Press any key...
*/

Por padrão, o tipo concreto DataProtectionProvider não criptografa o material da chave em bruto antes de armazená-lo no sistema de arquivos. Isso é para suportar cenários em que o desenvolvedor aponta para um compartilhamento de rede e o sistema de Proteção de Dados não pode deduzir automaticamente um mecanismo de criptografia de chave em repouso apropriado.

Além disso, o tipo concreto DataProtectionProvider não isolar aplicativos por padrão. Todos os aplicativos que usam o mesmo diretório de chave podem compartilhar cargas úteis, desde que seus parâmetros de finalidade correspondam.

O construtor DataProtectionProvider aceita uma função de retorno de configuração opcional que pode ser usada para ajustar o comportamento do sistema. O exemplo abaixo demonstra a restauração do isolamento com uma chamada explícita para SetApplicationName. O exemplo também demonstra a configuração do sistema para criptografar automaticamente chaves persistentes usando DPAPI do Windows. Se o diretório apontar para um compartilhamento UNC, talvez você queira distribuir um certificado compartilhado em todas as máquinas relevantes e configurar o sistema para usar criptografia baseada em certificado com uma chamada para ProtectKeysWithCertificate.

using System;
using System.IO;
using Microsoft.AspNetCore.DataProtection;

public class Program
{
    public static void Main(string[] args)
    {
        // Get the path to %LOCALAPPDATA%\myapp-keys
        var destFolder = Path.Combine(
            System.Environment.GetEnvironmentVariable("LOCALAPPDATA"),
            "myapp-keys");

        // Instantiate the data protection system at this folder
        var dataProtectionProvider = DataProtectionProvider.Create(
            new DirectoryInfo(destFolder),
            configuration =>
            {
                configuration.SetApplicationName("my app name");
                configuration.ProtectKeysWithDpapi();
            });

        var protector = dataProtectionProvider.CreateProtector("Program.No-DI");
        Console.Write("Enter input: ");
        var input = Console.ReadLine();

        // Protect the payload
        var protectedPayload = protector.Protect(input);
        Console.WriteLine($"Protect returned: {protectedPayload}");

        // Unprotect the payload
        var unprotectedPayload = protector.Unprotect(protectedPayload);
        Console.WriteLine($"Unprotect returned: {unprotectedPayload}");

        Console.WriteLine();
        Console.WriteLine("Press any key...");
        Console.ReadKey();
    }
}

Dica

Instâncias do tipo concreto DataProtectionProvider são caras de criar. Se um aplicativo mantiver várias instâncias desse tipo e se todas estiverem usando o mesmo diretório de armazenamento de chaves, o desempenho do aplicativo poderá se degradar. Se você usar o tipo DataProtectionProvider, recomendamos que você crie esse tipo uma vez e o reutilize o máximo possível. O tipo DataProtectionProvider e todas as instâncias IDataProtector criadas a partir desse tipo são seguras para uso com múltiplos chamadores.