Configurar a proteção de dados do ASP.NET Core

Quando o sistema de Proteção de Dados é inicializado, ele aplica as configurações padrão com base no ambiente operacional. Essas configurações geralmente são apropriadas para aplicativos em execução em um único computador. Há casos em que um desenvolvedor pode querer alterar as configurações padrão:

  • O aplicativo é distribuído em vários máquinas.
  • Por motivos de conformidade.

Para esses cenários, o sistema de Proteção de Dados oferece uma API de configuração avançada.

Aviso

Semelhante aos arquivos de configuração, o anel de chave de proteção de dados deve ser protegido usando as permissões apropriadas. Você pode optar por criptografar chaves em repouso, mas isso não impede que os invasores criam novas chaves. Consequentemente, a segurança do aplicativo é impactada. O local de armazenamento configurado com a Proteção de Dados deve ter seu acesso limitado ao próprio aplicativo, semelhante à maneira como você protegeria os arquivos de configuração. Por exemplo, se você optar por armazenar o anel de chave no disco, use permissões do sistema de arquivos. Verifique se apenas a identidade sob a qual seu aplicativo Web é executado tem acesso de leitura, gravação e criação a esse diretório. Se você usar o Azure Blob Armazenamento, somente o aplicativo Web deverá ter a capacidade de ler, gravar ou criar novas entradas no armazenamento de blob etc.

O método de extensão AddDataProtection retorna um IDataProtectionBuilder. IDataProtectionBuilder expõe métodos de extensão que você pode encadear para configurar opções de Proteção de Dados.

Os seguintes NuGet pacotes são necessários para as extensões de Proteção de Dados usadas neste artigo:

ProtectKeysWithAzureKeyVault

Faça logoff no Azure usando a CLI, por exemplo:

az login

Para armazenar chaves no Azure Key Vault, configure o sistema com ProtectKeysWithAzureKeyVault na Startup classe . blobUriWithSasToken é o URI completo em que o arquivo de chave deve ser armazenado. O URI deve conter o token SAS como um parâmetro de cadeia de caracteres de consulta:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToAzureBlobStorage(new Uri("<blobUriWithSasToken>"))
        .ProtectKeysWithAzureKeyVault(new Uri("<keyIdentifier>"), new DefaultAzureCredential());
}

Definir o local de armazenamento do anel de chave (por exemplo, PersistKeysToAzureBlobStorage). O local deve ser definido porque chamar implementa um ProtectKeysWithAzureKeyVault IXmlEncryptor que desabilita as configurações automáticas de proteção de dados, incluindo o local de armazenamento do anel de chave. O exemplo anterior usa o blob do Azure Armazenamento para persistir o anel de chave. Para obter mais informações, consulte Provedores de armazenamento de chaves: Azure Armazenamento. Você também pode persistir o anel de chave localmente com PersistKeysToFileSystem.

O keyIdentifier é o identificador de chave do cofre de chaves usado para criptografia de chave. Por exemplo, uma chave criada no cofre de chaves dataprotection chamada no tem o contosokeyvault identificador de chave https://contosokeyvault.vault.azure.net/keys/dataprotection/ . Forneça ao aplicativo as permissões Unwrap Key e Wrap Key para o cofre de chaves.

ProtectKeysWithAzureKeyVault Sobrecargas:

Se o aplicativo usar os pacotes anteriores do Azure ( e ) e uma combinação de Azure Key Vault e Microsoft.AspNetCore.DataProtection.AzureStorage Armazenamento do Azure para armazenar e proteger chaves, será lançada se o blob para armazenamento de chaves não Microsoft.AspNetCore.DataProtection.AzureKeyVault System.UriFormatException existir. O blob pode ser criado manualmente antes de executar o aplicativo no portal do Azure ou usar o seguinte procedimento:

  1. Remova a chamada para ProtectKeysWithAzureKeyVault para a primeira executar para criar o blob no local.
  2. Adicione a chamada a ProtectKeysWithAzureKeyVault para as próximas executações.

É aconselhável remover para a primeira operação, pois garante que o arquivo seja criado com o esquema e os valores ProtectKeysWithAzureKeyVault adequados em funcionamento.

Recomendamos atualizar para os pacotes Azure.Extensions.AspNetCore.DataProtection.Blobs e Azure.Extensions.AspNetCore.DataProtection.Keys porque a API fornecida cria automaticamente o blob se ele não existir.

services.AddDataProtection()
    //This blob must already exist before the application is run
    .PersistKeysToAzureBlobStorage("<storage account connection string", "<key store container name>", "<key store blob name>")
    //Removing this line below for an initial run will ensure the file is created correctly
    .ProtectKeysWithAzureKeyVault(new Uri("<keyIdentifier>"), new DefaultAzureCredential());

PersistKeysToFileSystem

Para armazenar chaves em um compartilhamento UNC em vez de no local padrão %LOCALAPPDATA%, configure o sistema com PersistKeysToFileSystem:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"));
}

Aviso

Se você alterar o local de persistência de chave, o sistema não criptografa automaticamente as chaves em repouso, pois não sabe se o DPAPI é um mecanismo de criptografia apropriado.

PersistKeysToDbContext

Para armazenar chaves em um banco de dados usando EntityFramework, configure o sistema com o pacote Microsoft.AspNetCore.DataProtection.EntityFrameworkCore:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToDbContext<DbContext>()
}

O código anterior armazena as chaves no banco de dados configurado. O contexto do banco de dados que está sendo usado deve implementar IDataProtectionKeyContext . IDataProtectionKeyContext expõe a propriedade DataProtectionKeys

public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }

Essa propriedade representa a tabela na qual as chaves são armazenadas. Crie a tabela manualmente ou com DbContext Migrações. Consulte DataProtectionKey para obter mais informações.

ProtectKeysWith*

Você pode configurar o sistema para proteger as chaves em repouso chamando qualquer uma das APIs de configuração ProtectKeysWith. * Considere o exemplo abaixo, que armazena chaves em um compartilhamento UNC e criptografa essas chaves em repouso com um certificado X.509 específico:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
        .ProtectKeysWithCertificate(Configuration["Thumbprint"]);
}

No ASP.NET Core 2.1 ou posterior, você pode fornecer um X509Certificate2 para ProtectKeysWithCertificate, como um certificado carregado de um arquivo:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
        .ProtectKeysWithCertificate(
            new X509Certificate2("certificate.pfx", Configuration["Thumbprint"]));
}

Consulte Criptografia de chave em repouso para obter mais exemplos e discussão sobre os mecanismos de criptografia de chave interna.

UnprotectKeysWithAnyCertificate

No ASP.NET Core 2.1 ou posterior, você pode girar certificados e descriptografar chaves em repouso usando uma matriz de certificados X509Certificate2 com UnprotectKeysWithAnyCertificate:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
        .ProtectKeysWithCertificate(
            new X509Certificate2("certificate.pfx", Configuration["MyPasswordKey"));
        .UnprotectKeysWithAnyCertificate(
            new X509Certificate2("certificate_old_1.pfx", Configuration["MyPasswordKey_1"]),
            new X509Certificate2("certificate_old_2.pfx", Configuration["MyPasswordKey_2"]));
}

SetDefaultKeyLifetime

Para configurar o sistema para usar um tempo de vida de chave de 14 dias em vez dos 90 dias padrão, use SetDefaultKeyLifetime:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .SetDefaultKeyLifetime(TimeSpan.FromDays(14));
}

SetApplicationName

Por padrão, o sistema de Proteção de Dados isola aplicativos um do outro com base em seus caminhos raiz de conteúdo, mesmo se eles estão compartilhando o mesmo repositório de chaves físicas. Isso impede que os aplicativos entendam os payloads protegidos uns dos outros.

Para compartilhar cargas protegidas entre aplicativos:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .SetApplicationName("shared app name");
}

DisableAutomaticKeyGeneration

Você pode ter um cenário em que não deseja que um aplicativo role automaticamente as chaves (crie novas chaves) conforme elas se aproximam da expiração. Um exemplo disso pode ser aplicativos definidos em uma relação primária/secundária, em que apenas o aplicativo primário é responsável por questões de gerenciamento de chaves e os aplicativos secundários simplesmente têm uma exibição somente leitura do anel de chave. Os aplicativos secundários podem ser configurados para tratar o anel de chave como somente leitura configurando o sistema com DisableAutomaticKeyGeneration :

public void ConfigureServices(IServiceCollection services)
{
    services.AddDataProtection()
        .DisableAutomaticKeyGeneration();
}

Isolamento por aplicativo

Quando o sistema de Proteção de Dados é fornecido por um host ASP.NET Core, ele isola automaticamente os aplicativos um do outro, mesmo que esses aplicativos sejam executados na mesma conta de processo de trabalho e usem o mesmo material de chave mestra. Isso é um pouco semelhante ao modificador IsolateApps do elemento <machineKey> System.Web.

O mecanismo de isolamento funciona considerando cada aplicativo no computador local como um locatário exclusivo, portanto, a raiz de qualquer determinado aplicativo inclui automaticamente a ID do aplicativo como IDataProtector discriminador. A ID exclusiva do aplicativo é o caminho físico do aplicativo:

  • Para aplicativos hospedados no IIS, a ID exclusiva é o caminho físico do IIS do aplicativo. Se um aplicativo for implantado em um ambiente de web farm, esse valor será estável supondo que os ambientes do IIS sejam configurados da mesma forma em todos os máquinas no web farm.
  • Para aplicativos auto-hospedados em execução no Kestrel servidor, a ID exclusiva é o caminho físico para o aplicativo no disco.

O identificador exclusivo foi projetado para sobreviver a redefinições — do aplicativo individual e do próprio computador.

Esse mecanismo de isolamento pressu que os aplicativos não são mal-intencionados. Um aplicativo mal-intencionado sempre pode afetar qualquer outro aplicativo em execução na mesma conta de processo de trabalho. Em um ambiente de hospedagem compartilhado em que os aplicativos são mutuamente não confiados, o provedor de hospedagem deve tomar medidas para garantir o isolamento no nível do sistema operacional entre aplicativos, incluindo a separação dos repositórios de chaves subjacentes dos aplicativos.

Se o sistema de Proteção de Dados não for fornecido por um host ASP.NET Core (por exemplo, se você instanciá-lo por meio do tipo concreto), o isolamento do aplicativo será desabilitado DataProtectionProvider por padrão. Quando o isolamento do aplicativo é desabilitado, todos os aplicativos apoiados pelo mesmo material de chave podem compartilhar conteúdos, desde que forneçam as finalidades apropriadas. Para fornecer isolamento de aplicativo nesse ambiente, chame o método SetApplicationName no objeto de configuração e forneça um nome exclusivo para cada aplicativo.

Proteção de dados e isolamento do aplicativo

Considere o seguinte para isolamento do aplicativo:

  • Quando vários aplicativos são apontados para o mesmo repositório de chaves, a intenção é que os aplicativos compartilhem o mesmo material de chave mestra. A Proteção de Dados é desenvolvida com a suposição de que todos os aplicativos que compartilham um anel de chave podem acessar todos os itens nesse anel de chave. O identificador exclusivo do aplicativo é usado para isolar chaves específicas do aplicativo derivadas das chaves fornecidas pelo anel de chave. Ele não espera que permissões de nível de item, como aquelas fornecidas pelo Azure KeyVault, sejam usadas para impor isolamento extra. A tentativa de permissões de nível de item gera erros de aplicativo. Se você não quiser depender do isolamento do aplicativo integrado, os locais de armazenamento de chaves separados deverão ser usados e não compartilhados entre aplicativos.

  • O discriminador de aplicativo é usado para permitir que diferentes aplicativos compartilhem o mesmo material de chave mestra, mas para manter seus conteúdos criptográficos distintos entre si. Para que os aplicativos sejam capazes de ler os payloads criptográficos uns dos outros, eles devem ter o mesmo discriminador de aplicativo.

  • Se um aplicativo for comprometido (por exemplo, por um ataque de RCE), todo o material de chave mestra acessível a esse aplicativo também deverá ser considerado comprometido, independentemente de seu estado de proteção em repouso. Isso implica que, se dois aplicativos são apontados para o mesmo repositório, mesmo que usem discriminadores de aplicativos diferentes, um comprometimento de um deles é funcionalmente equivalente a um comprometimento de ambos.

    Essa cláusula "funcionalmente equivalente a um comprometimento de ambos", mesmo que os dois aplicativos usem mecanismos diferentes para proteção de chave em repouso. Normalmente, essa não é uma configuração esperada. O mecanismo de proteção em repouso destina-se a fornecer proteção caso um adversário tenha acesso de leitura ao repositório. Um adversário que obtém acesso de gravação ao repositório (talvez porque ele tenha obtido a permissão de execução de código dentro de um aplicativo) pode inserir chaves mal-intencionadas no armazenamento. O sistema de Proteção de Dados intencionalmente não fornece proteção contra um adversário que obtém acesso de gravação ao repositório de chaves.

  • Se os aplicativos precisam permanecer realmente isolados um do outro, eles devem usar repositórios de chaves diferentes. Isso naturalmente fica fora da definição de "isolado". Os aplicativos não serão isolados se todos eles têm acesso de Leitura e Gravação aos armazenamentos de dados uns dos outros.

Alterando algoritmos com UseCryptographicAlgorithms

A pilha Proteção de Dados permite que você altere o algoritmo padrão usado pelas chaves recém-geradas. A maneira mais simples de fazer isso é chamar UseCryptographicAlgorithms do retorno de chamada de configuração:

services.AddDataProtection()
    .UseCryptographicAlgorithms(
        new AuthenticatedEncryptorConfiguration()
    {
        EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
        ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
    });
services.AddDataProtection()
    .UseCryptographicAlgorithms(
        new AuthenticatedEncryptionSettings()
    {
        EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
        ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
    });

O EncryptionAlgorithm padrão é AES-256-CBC e o ValidationAlgorithm padrão é HMACSHA256. A política padrão pode ser definida por um administrador do sistema por meio de uma política em todo o computador, mas uma chamada explícita para substitui a UseCryptographicAlgorithms política padrão.

Chamar UseCryptographicAlgorithms permite que você especifique o algoritmo desejado de uma lista criada predefinida. Você não precisa se preocupar com a implementação do algoritmo. No cenário acima, o sistema de Proteção de Dados tenta usar a implementação CNG do AES se estiver em execução Windows. Caso contrário, ele voltará para a classe System.Security.Cryptography.Aes gerenciada.

Você pode especificar manualmente uma implementação por meio de uma chamada para UseCustomCryptographicAlgorithms.

Dica

Alterar algoritmos não afeta as chaves existentes no anel de chave. Ela afeta apenas as chaves recém-geradas.

Especificando algoritmos gerenciados personalizados

Para especificar algoritmos gerenciados personalizados, crie uma instância ManagedAuthenticatedEncryptorConfiguration que aponta para os tipos de implementação:

serviceCollection.AddDataProtection()
    .UseCustomCryptographicAlgorithms(
        new ManagedAuthenticatedEncryptorConfiguration()
    {
        // A type that subclasses SymmetricAlgorithm
        EncryptionAlgorithmType = typeof(Aes),

        // Specified in bits
        EncryptionAlgorithmKeySize = 256,

        // A type that subclasses KeyedHashAlgorithm
        ValidationAlgorithmType = typeof(HMACSHA256)
    });

Para especificar algoritmos gerenciados personalizados, crie uma instância ManagedAuthenticatedEncryptionSettings que aponta para os tipos de implementação:

serviceCollection.AddDataProtection()
    .UseCustomCryptographicAlgorithms(
        new ManagedAuthenticatedEncryptionSettings()
    {
        // A type that subclasses SymmetricAlgorithm
        EncryptionAlgorithmType = typeof(Aes),

        // Specified in bits
        EncryptionAlgorithmKeySize = 256,

        // A type that subclasses KeyedHashAlgorithm
        ValidationAlgorithmType = typeof(HMACSHA256)
    });

Geralmente, as propriedades Type devem apontar para implementações concretas e instanciáveis (por meio de um ctor sem * parâmetros público) de SymmetricAlgorithm e KeyedHashAlgorithm,embora o sistema tenha casos especiais de alguns valores, como typeof(Aes) por conveniência.

Observação

O SymmetricAlgorithm deve ter um comprimento de chave de ≥ 128 bits e um tamanho de bloco de ≥ 64 bits e deve dar suporte à criptografia de modo CBC com preenchimento PKCS #7. O KeyedHashAlgorithm deve ter um tamanho digest de >= 128 bits e deve dar suporte a chaves de comprimento iguais ao comprimento do resumo do algoritmo de hash. O KeyedHashAlgorithm não é estritamente necessário para ser HMAC.

Especificando algoritmos Windows CNG personalizados

Para especificar um algoritmo CNG de Windows personalizado usando criptografia de modo CBC com validação HMAC, crie uma instância CngCbcAuthenticatedEncryptorConfiguration que contém as informações algorítmicas:

services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(
        new CngCbcAuthenticatedEncryptorConfiguration()
    {
        // Passed to BCryptOpenAlgorithmProvider
        EncryptionAlgorithm = "AES",
        EncryptionAlgorithmProvider = null,

        // Specified in bits
        EncryptionAlgorithmKeySize = 256,

        // Passed to BCryptOpenAlgorithmProvider
        HashAlgorithm = "SHA256",
        HashAlgorithmProvider = null
    });

Para especificar um algoritmo CNG de Windows personalizado usando criptografia de modo CBC com validação HMAC, crie uma instância CngCbcAuthenticatedEncryptionSettings que contém as informações algorítmicas:

services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(
        new CngCbcAuthenticatedEncryptionSettings()
    {
        // Passed to BCryptOpenAlgorithmProvider
        EncryptionAlgorithm = "AES",
        EncryptionAlgorithmProvider = null,

        // Specified in bits
        EncryptionAlgorithmKeySize = 256,

        // Passed to BCryptOpenAlgorithmProvider
        HashAlgorithm = "SHA256",
        HashAlgorithmProvider = null
    });

Observação

O algoritmo de criptografia de bloco simétrico deve ter um comprimento de chave de >= 128 bits, um tamanho de bloco de >= 64 bits e deve dar suporte à criptografia de modo CBC com preenchimento #7 PKCS. O algoritmo de hash deve ter um tamanho de resumo de >= 128 bits e deve dar suporte à abertura com o sinalizador BCRYPT _ ALG _ HANDLE _ HMAC _ FLAG. As * propriedades do provedor podem ser definidas como null para usar o provedor padrão para o algoritmo especificado. Consulte a documentação BCryptOpenAlgorithmProvider para obter mais informações.

Para especificar um algoritmo CNG de Windows personalizado usando a criptografia de Modo de Contador/Decíconfiguração com validação, crie uma instância CngGcmAuthenticatedEncryptorConfiguration que contém as informações algorítmicas:

services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(
        new CngGcmAuthenticatedEncryptorConfiguration()
    {
        // Passed to BCryptOpenAlgorithmProvider
        EncryptionAlgorithm = "AES",
        EncryptionAlgorithmProvider = null,

        // Specified in bits
        EncryptionAlgorithmKeySize = 256
    });

Para especificar um algoritmo CNG de Windows personalizado usando a criptografia de Modo de Contador/Recurso de Conta, crie uma instância CngGcmAuthenticatedEncryptionSettings que contém as informações algorítmicas:

services.AddDataProtection()
    .UseCustomCryptographicAlgorithms(
        new CngGcmAuthenticatedEncryptionSettings()
    {
        // Passed to BCryptOpenAlgorithmProvider
        EncryptionAlgorithm = "AES",
        EncryptionAlgorithmProvider = null,

        // Specified in bits
        EncryptionAlgorithmKeySize = 256
    });

Observação

O algoritmo de criptografia de bloco simétrico deve ter um comprimento de chave de >= 128 bits, um tamanho de bloco de exatamente 128 bits e deve dar suporte à criptografia GCM. Você pode definir a propriedade EncryptionAlgorithmProvider como nula para usar o provedor padrão para o algoritmo especificado. Consulte a documentação BCryptOpenAlgorithmProvider para obter mais informações.

Especificando outros algoritmos personalizados

Embora não seja exposto como uma API de primeira classe, o sistema de Proteção de Dados é extensível o suficiente para permitir a especificação de quase qualquer tipo de algoritmo. Por exemplo, é possível manter todas as chaves contidas em um HSM (Módulo de Segurança de Hardware) e fornecer uma implementação personalizada das rotinas de criptografia e descriptografia principais. Consulte IAuthenticatedEncryptor em Extensibilidade de criptografia principal para obter mais informações.

Persistir chaves ao hospedar em um contêiner do Docker

Ao hospedar em um contêiner do Docker, as chaves devem ser mantidas em:

Persistir chaves com Redis

Somente as versões do Redis que suportam a Persistência de Dados do Redis devem ser usadas para armazenar chaves. O Armazenamento de Blob do Azure é persistente e pode ser usado para armazenar chaves. Saiba mais neste tópico do GitHub.

Registro em log de DataProtection

InformationHabilita o registro em log de nível de DataProtection para ajudar a diagnosticar o problema. O seguinte appsetting.jsno arquivo habilita o log de informações da API DataProtection:

{
  "Logging": {
    "LogLevel": {
      "Microsoft.AspNetCore.DataProtection": "Information"
    }
  }
}

Para obter mais informações sobre registro em log, consulte Registro em log no .NET Core e ASP.NET Core.

Recursos adicionais