Provedores de armazenamento de chaves no ASP.NET Core

O sistema de proteção de dados emprega um mecanismo de descoberta por padrão para determinar onde as chaves criptográficas devem ser persistidas. O desenvolvedor pode substituir o mecanismo de descoberta padrão e especificar manualmente o local.

Aviso

Se você especificar um local de persistência de chave explícita, o sistema de proteção de dados e cancela o registro do mecanismo de criptografia de chave em repouso padrão, para que as chaves não sejam mais criptografadas em repouso. É recomendável que você especifique adicionalmente um mecanismo explícito de criptografia de chave para implantações de produção.

Sistema de arquivos

Para configurar um repositório de chaves baseado no sistema de arquivos, chame a rotina de configuração PersistKeysToFileSystem, conforme mostrado abaixo. Forneça um DirectoryInfo apontando para o repositório onde as chaves devem ser armazenadas:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"c:\temp-keys\"));
}

O DirectoryInfo pode apontar para um diretório no computador local ou pode apontar para uma pasta em um compartilhamento de rede. Se estiver apontando para um diretório no computador local (e o cenário é que apenas aplicativos no computador local exigem acesso para usar esse repositório), considere usar o DPAPI do Windows (no Windows) para criptografar as chaves em repouso. Caso contrário, considere usar um certificado X.509 para criptografar chaves em repouso.

Armazenamento do Azure

O pacote Azure.Extensions.AspNetCore.DataProtection.Blobs permite armazenar chaves de proteção de dados em Armazenamento de Blobs do Azure. As chaves podem ser compartilhadas em várias instâncias de um aplicativo Web. Os aplicativos podem compartilhar cookies de autenticação ou a proteção CSRF em vários servidores.

Para configurar o provedor de Armazenamento de Blobs do Azure, chame uma das sobrecargas do PersistKeysToAzureBlobStorage.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToAzureBlobStorage(new Uri("<blob URI including SAS token>"));
}

Se o aplicativo Web estiver em execução como um serviço do Azure, a cadeia de conexão poderá ser usada para autenticar no armazenamento do Azure por meio de Azure.Storage.Blobs.

string connectionString = "<connection_string>";
string containerName = "my-key-container";
string blobName = "keys.xml";
BlobContainerClient container = new BlobContainerClient(connectionString, containerName);

// optional - provision the container automatically
await container.CreateIfNotExistsAsync();

BlobClient blobClient = container.GetBlobClient(blobName);

services.AddDataProtection()
    .PersistKeysToAzureBlobStorage(blobClient);

Observação

A cadeia de conexão com sua conta de armazenamento pode ser encontrada no Portal do Azure na seção "Chaves de Acesso" ou executando o seguinte comando da CLI:

az storage account show-connection-string --name <account_name> --resource-group <resource_group>

Redis

O pacote Microsoft.AspNetCore.DataProtection.StackExchangeRedis permite armazenar chaves de proteção de dados em um cache Redis. As chaves podem ser compartilhadas em várias instâncias de um aplicativo Web. Os aplicativos podem compartilhar cookies de autenticação ou a proteção CSRF em vários servidores.

O pacote Microsoft.AspNetCore.DataProtection.StackExchangeRedis permite armazenar chaves de proteção de dados em um cache Redis. As chaves podem ser compartilhadas em várias instâncias de um aplicativo Web. Os aplicativos podem compartilhar cookies de autenticação ou a proteção CSRF em vários servidores.

Para configurar no Redis, chame uma das sobrecarga PersistKeysToStackExchangeRediss:

public void ConfigureServices(IServiceCollection services)
{
    var redis = ConnectionMultiplexer.Connect("<URI>");
    services.AddDataProtection()
        .PersistKeysToStackExchangeRedis(redis, "DataProtection-Keys");
}

Para configurar no Redis, chame uma das sobrecarga PersistKeysToRediss:

public void ConfigureServices(IServiceCollection services)
{
    var redis = ConnectionMultiplexer.Connect("<URI>");
    services.AddDataProtection()
        .PersistKeysToRedis(redis, "DataProtection-Keys");
}

Para obter mais informações, consulte estes tópicos:

Registro

Aplica-se somente a implantações do Windows.

Às vezes, o aplicativo pode não ter acesso de gravação ao sistema de arquivos. Considere um cenário em que um aplicativo está sendo executado como uma conta de serviço virtual (como a identidade do pool de aplicativos de w3wp.exe). Nesses casos, o administrador pode provisionar uma chave do Registro acessível pela identidade da conta de serviço. Chame o método de extensão PersistKeysToRegistry, conforme mostrado abaixo. Forneça um RegistryKey apontando para o local onde as chaves criptográficas devem ser armazenadas:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToRegistry(Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Sample\keys", true));
}

Importante

É recomendável usar o DPAPI do Windows para criptografar as chaves em repouso.

Entity Framework Core

O pacote Microsoft.AspNetCore.DataProtection.EntityFrameworkCore fornece um mecanismo para armazenar chaves de proteção de dados em um banco de dados usando o Entity Framework Core. O pacote NuGet Microsoft.AspNetCore.DataProtection.EntityFrameworkCore deve ser adicionado ao arquivo de projeto, não faz parte do metapacote Microsoft.AspNetCore.App.

Com esse pacote, as chaves podem ser compartilhadas entre várias instâncias de um aplicativo Web.

Para configurar o provedor EF Core, chame o método PersistKeysToDbContext:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));

    // Add a DbContext to store your Database Keys
    services.AddDbContext<MyKeysContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("MyKeysConnection")));

    // using Microsoft.AspNetCore.DataProtection;
    services.AddDataProtection()
        .PersistKeysToDbContext<MyKeysContext>();

    services.AddDefaultIdentity<IdentityUser>()
        .AddDefaultUI(UIFramework.Bootstrap4)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

Se você quiser ver os comentários de código traduzidos para idiomas diferentes do inglês, informe-nos neste problema de discussão do GitHub.

O parâmetro genérico TContext deve herdar de DbContext e implementar IDataProtectionKeyContext:

using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using WebApp1.Data;

namespace WebApp1
{
    class MyKeysContext : DbContext, IDataProtectionKeyContext
    {
        // A recommended constructor overload when using EF Core 
        // with dependency injection.
        public MyKeysContext(DbContextOptions<MyKeysContext> options) 
            : base(options) { }

        // This maps to the table that stores keys.
        public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }
    }
}

Criar a tabela DataProtectionKeys.

Execute os seguintes comandos na janela do Console do Gerenciador de Pacotes (PMC):

Add-Migration AddDataProtectionKeys -Context MyKeysContext
Update-Database -Context MyKeysContext

MyKeysContext é o DbContext definido no exemplo de código anterior. Se você estiver usando um DbContext com um nome diferente, substitua seu nome de DbContext por MyKeysContext.

A classe/entidade DataProtectionKeys adota a estrutura mostrada na tabela a seguir.

Propriedade/Campo Tipo CLR Tipo SQL
Id int int, PK, IDENTITY(1,1), não nulo
FriendlyName string nvarchar(MAX), nulo
Xml string nvarchar(MAX), nulo

Repositório de chaves personalizadas

Se os mecanismos internos não forem apropriados, o desenvolvedor poderá especificar seu próprio mecanismo de persistência de chave fornecendo um IXmlRepository personalizado.