Partilhar via


Principais provedores de armazenamento 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 persistentes. O desenvolvedor pode substituir o mecanismo de descoberta padrão e especificar manualmente o local.

Advertência

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

Sistema de ficheiros

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 na máquina local ou pode apontar para uma pasta em um compartilhamento de rede. Se estiver a apontar para um diretório na máquina local (e o cenário é que apenas as aplicações na máquina local requerem acesso para usar esse repositório), considere usar DPAPI do Windows (no Windows) para criptografar as chaves em descanso. Caso contrário, considere usar um certificado X.509 para criptografar chaves em repouso.

Armazenamento do Azure

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

Observação

Para obter orientação sobre como adicionar pacotes a aplicativos .NET, consulte os artigos na seção Instalar e gerenciar pacotes em Workflow de utilização de pacotes (documentação do NuGet). Confirme as versões corretas do pacote em NuGet.org.

Para interagir com o Azure Key Vault localmente usando credenciais de desenvolvedor, entre em sua conta de armazenamento no Visual Studio ou entre com a CLI do Azure. Se você ainda não instalou a CLI do Azure, consulte Como instalar a CLI do Azure. Você pode executar o seguinte comando no painel Developer PowerShell no Visual Studio ou em um shell de comando quando não estiver usando o Visual Studio:

az login

Para obter mais informações, consulte Entrar no Azure usando ferramentas de desenvolvedor.

Configure o Armazenamento de Blobs do Azure para manter as chaves de proteção de dados:

  • Crie uma conta de armazenamento do Azure.

  • Crie um contêiner para armazenar o arquivo de chave de proteção de dados.

  • Recomendamos usar Azure Managed Identity e o controle de acesso baseado em função (RBAC) para acessar o blob de armazenamento de chaves. Não é necessário criar um arquivo de chave e carregá-lo no contêiner da conta de armazenamento. A estrutura cria o arquivo para você. Para inspecionar o conteúdo de um arquivo de chave, use o comando View/edit do menu de contexto no final de uma linha de chave no portal.

! [OBSERVAÇÃO] Se você planeja usar um URI de blob com uma assinatura de acesso compartilhado (SAS) em vez de um gerenciado Identity, use um editor de texto para criar um arquivo de chave XML em sua máquina local:

<?xml version="1.0" encoding="utf-8"?>
<repository>
</repository>

Carregue o arquivo de chave para o contêiner da conta de armazenamento. Use o comando Exibir/editar do menu de contexto no final da linha de teclas no portal para confirmar se o blob contém o conteúdo anterior. Ao criares o ficheiro manualmente, és capaz de obter o URI do blob com SAS do portal para configurar a aplicação numa etapa posterior.

  • Crie um Azure Managed Identity (ou adicione uma função ao Managed Identity existente que você planeja usar) com a função Storage Blob Data Contributor . Atribua o Gerenciado Identity ao Serviço de Aplicativo do Azure que está hospedando a implantação: Configurações>Identity>Adição atribuída> pelo usuário.

    Observação

    Se também planeia executar uma aplicação localmente com um utilizador autorizado para acesso a blobs usando a CLI do Azure ou a autenticação de serviço do Azure no Visual Studio, adicione a sua conta de utilizador do Azure do desenvolvedor no Control de Acesso (IAM) com a função de Contribuidor de Dados de Blob de Armazenamento. Se você quiser usar a CLI do Azure por meio do Visual Studio, execute o az login comando no painel Developer PowerShell e siga os prompts para autenticar com o locatário.

Para configurar o provedor de Armazenamento de Blob do Azure, chame uma das PersistKeysToAzureBlobStorage sobrecargas no aplicativo. O exemplo a seguir usa a sobrecarga que aceita um URI de blob e uma credencial de token (TokenCredential), confiando num sistema gerido pela Azure para controle de acesso baseado em função (RBAC).

Outras sobrecargas baseiam-se em:

  • Um URI de blob e uma credencial de chave compartilhada de armazenamento (StorageSharedKeyCredential).
  • Um URI de blob com uma assinatura de acesso compartilhado (SAS).
  • Uma cadeia de conexão, nome do contêiner e nome do blob.
  • Um cliente de blob (BlobClient).

Para obter mais informações sobre a API e a autenticação do SDK do Azure, consulte Autenticar aplicativos .NET para serviços do Azure usando a biblioteca do AzureIdentity. Para obter orientações de registo, consulte Registo com o SDK do Azure para .NET: Registo sem registo de cliente. Para aplicações que usam injeção de dependência, uma aplicação pode chamar AddAzureClientsCore, passando true para enableLogForwarding, para criar e conectar a infraestrutura de registro.

No ficheiro onde os Program serviços estão registados:

TokenCredential? credential;

if (builder.Environment.IsProduction())
{
    credential = new ManagedIdentityCredential("{MANAGED IDENTITY CLIENT ID}");
}
else
{
    // Local development and testing only
    DefaultAzureCredentialOptions options = new()
    {
        // Specify the tenant ID to use the dev credentials when running the app locally
        // in Visual Studio.
        VisualStudioTenantId = "{TENANT ID}",
        SharedTokenCacheTenantId = "{TENANT ID}"
    };

    credential = new DefaultAzureCredential(options);
}

builder.Services.AddDataProtection()
    .SetApplicationName("{APPLICATION NAME}")
    .PersistKeysToAzureBlobStorage(new Uri("{BLOB URI}"), credential);

{MANAGED IDENTITY CLIENT ID}: A ID do Cliente Gerenciado Identity do Azure (GUID).

{TENANT ID}: ID do inquilino.

{APPLICATION NAME}: SetApplicationName define o nome exclusivo deste aplicativo dentro do sistema de proteção de dados. O valor deve corresponder entre as implantações do aplicativo.

{BLOB URI}: URI completo para o arquivo de chave. O URI é gerado pelo Armazenamento do Azure quando você cria o arquivo de chave. Não utilize uma SAS.

Abordagem alternativa de assinatura de acesso compartilhado (SAS): Como alternativa ao uso de um Gerenciado Identity para acesso ao blob de chave no Armazenamento de Blobs do Azure, você pode chamar a PersistKeysToAzureBlobStorage sobrecarga que aceita um URI de blob com um token SAS:

builder.Services.AddDataProtection()
    .SetApplicationName("{APPLICATION NAME}")
    .PersistKeysToAzureBlobStorage(new Uri("{BLOB URI WITH SAS}"));

Em Startup.ConfigureServices:

TokenCredential? credential;

if (_env.IsProduction())
{
    credential = new ManagedIdentityCredential("{MANAGED IDENTITY CLIENT ID}");
}
else
{
    // Local development and testing only
    DefaultAzureCredentialOptions options = new()
    {
        // Specify the tenant ID to use the dev credentials when running the app locally
        // in Visual Studio.
        VisualStudioTenantId = "{TENANT ID}",
        SharedTokenCacheTenantId = "{TENANT ID}"
    };

    credential = new DefaultAzureCredential(options);
}

services.AddDataProtection()
    .SetApplicationName("{APPLICATION NAME}")
    .PersistKeysToAzureBlobStorage(new Uri("{BLOB URI}"), credential);

{MANAGED IDENTITY CLIENT ID}: A ID do Cliente Gerenciado Identity do Azure (GUID).

{TENANT ID}: ID do inquilino.

{APPLICATION NAME}: SetApplicationName define o nome exclusivo deste aplicativo dentro do sistema de proteção de dados. O valor deve corresponder entre as implantações do aplicativo.

{BLOB URI}: URI completo para o arquivo de chave. O URI é gerado pelo Armazenamento do Azure quando você cria o arquivo de chave. Não utilize uma SAS.

Exemplo:

https://contoso.blob.core.windows.net/data-protection/keys.xml

Abordagem alternativa de assinatura de acesso compartilhado (SAS): Como alternativa ao uso de um Gerenciado Identity para acesso ao blob de chave no Armazenamento de Blobs do Azure, você pode chamar a PersistKeysToAzureBlobStorage sobrecarga que aceita um URI de blob com um token SAS:

services.AddDataProtection()
    .SetApplicationName("{APPLICATION NAME}")
    .PersistKeysToAzureBlobStorage(new Uri("{BLOB URI WITH SAS}"));

{APPLICATION NAME}: SetApplicationName define o nome exclusivo deste aplicativo dentro do sistema de proteção de dados. O valor deve corresponder entre as implantações do aplicativo.

{BLOB URI WITH SAS}: O URI completo onde o arquivo de chave deve ser armazenado com o token SAS como um parâmetro de cadeia de caracteres de consulta. O URI é gerado pelo Armazenamento do Azure quando você solicita uma SAS para o arquivo de chave carregado. No exemplo a seguir, o nome do contêiner é data-protection, e o nome da conta de armazenamento é contoso. O arquivo de chave é chamado keys.xml. A cadeia de caracteres de consulta da assinatura de acesso compartilhado (SAS) está no final do URI (espaço reservado {SHARED ACCESS SIGNATURE}).

Exemplo:

https://contoso.blob.core.windows.net/data-protection/keys.xml{SHARED ACCESS SIGNATURE}

Se a aplicação web estiver a ser executada como um serviço do Azure, pode ser utilizada uma cadeia de conexão para autenticar no Armazenamento do Azure, utilizando BlobContainerClient, como se vê no exemplo a seguir.

Advertência

Este artigo mostra o uso de cadeias de conexão. Com um banco de dados local, o usuário não precisa ser autenticado, mas na produção, as cadeias de conexão às vezes incluem uma senha para autenticar. Uma credencial de senha de proprietário de recurso (ROPC) é um risco de segurança que deve ser evitado em bancos de dados de produção. Os aplicativos de produção devem usar o fluxo de autenticação mais seguro disponível. Para obter mais informações sobre autenticação para aplicativos implantados em ambientes de teste ou produção, consulte Fluxos de autenticação segura.

A chamada opcional para CreateIfNotExistsAsync provisiona o contentor automaticamente se ele não existir.

A cadeia de conexão ({CONNECTION STRING} espaço reservado) para a conta de armazenamento pode ser encontrada no portal Entra ou no portal Azure na seção "Chaves de acesso" ou executando o seguinte comando da CLI do Azure:

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

No ficheiro onde os Program serviços estão registados:

string connectionString = "{CONNECTION STRING}";
string containerName = "{CONTAINER NAME}";
string blobName = "keys.xml";
var container = new BlobContainerClient(connectionString, containerName);
await container.CreateIfNotExistsAsync();
BlobClient blobClient = container.GetBlobClient(blobName);

builder.Services.AddDataProtection().PersistKeysToAzureBlobStorage(blobClient);

Em Startup.ConfigureServices:

string connectionString = "{CONNECTION STRING}";
string containerName = "{CONTAINER NAME}";
string blobName = "keys.xml";
var container = new BlobContainerClient(connectionString, containerName);
await container.CreateIfNotExistsAsync();
BlobClient blobClient = container.GetBlobClient(blobName);

services.AddDataProtection().PersistKeysToAzureBlobStorage(blobClient);

Redis

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

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

Para configurar no Redis, chame uma das PersistKeysToStackExchangeRedis sobrecargas:

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

Para configurar no Redis, chame uma das PersistKeysToRedis sobrecargas:

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

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

Registo

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

Às vezes, a aplicação pode não ter acesso de gravação ao sistema de arquivos. Considere um cenário em que um aplicativo esteja sendo executado como uma conta de serviço virtual (como a identidade do pool de aplicativos do 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 como 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

Recomendamos usar DPAPI do Windows para criptografar as chaves em repouso.

Núcleo do Entity Framework

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

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

Para configurar o provedor de 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ê gostaria de ver os comentários de código traduzidos para outros idiomas além do inglês, informe-nos em este 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; }
    }
}

Crie a tabela DataProtectionKeys.

Execute os seguintes comandos na janela Package Manager Console (PMC):

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

MyKeysContext é o DbContext definido no exemplo de código anterior. Se estiver a utilizar um DbContext com um nome diferente, substitua o seu nome 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 personalizado

Se os mecanismos da caixa de entrada não forem apropriados, o desenvolvedor poderá especificar seu próprio mecanismo de persistência de chave fornecendo um IXmlRepositorypersonalizado.