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 são apropriadas para aplicativos em execução em um único computador. No entanto, há casos em que um desenvolvedor pode querer alterar as configurações padrão:
- O aplicativo é distribuído em vários computadores.
- 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 rest, mas isso não impede que os invasores criem novas chaves. Consequentemente a segurança do aplicativo é afetada. 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 seu anel de chaves no disco, use permissões do sistema de arquivos. Verifique se apenas a identity na qual seu aplicativo Web é executado tem acesso de leitura, gravação e criação a esse diretório. Se você usar o Armazenamento de Blobs do Azure, somente o aplicativo Web deverá ter a capacidade de ler, gravar ou criar novas entradas no repositório de blobs 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.
Observação
Este artigo foi escrito para um aplicativo que é executado em um contêiner de janela de encaixe. Em um contêiner do Docker, o aplicativo sempre tem o mesmo caminho e, portanto, o mesmo discriminador de aplicativos. Os aplicativos que precisam ser executados em vários ambientes (por exemplo, local e implantado) devem definir o discriminador de aplicativos padrão para o ambiente. A execução de um aplicativo em vários ambientes está além do escopo deste artigo.
Os seguintes pacotes NuGet são necessários para as extensões de Proteção de Dados usadas neste artigo:
ProtectKeysWithAzureKeyVault
Entre no Azure usando a CLI, por exemplo:
az login
Para gerenciar chaves com a Azure Key Vault, configure o sistema com ProtectKeysWithAzureKeyVault no Program.cs
. 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:
builder.Services.AddDataProtection()
.PersistKeysToAzureBlobStorage(new Uri("<blobUriWithSasToken>"))
.ProtectKeysWithAzureKeyVault(new Uri("<keyIdentifier>"), new DefaultAzureCredential());
Para que um aplicativo se comunique e se autorize com o KeyVault, o pacote do AzureIdentity deve ser adicionado.
Defina o local de armazenamento do anel de chaves (por exemplo, PersistKeysToAzureBlobStorage). O local deve ser definido porque a chamada ProtectKeysWithAzureKeyVault
implementa um IXmlEncryptor que desabilita as configurações automáticas de proteção de dados, incluindo o local de armazenamento do anel de chaves. O exemplo anterior usa Armazenamento de Blobs do Azure para persistir o anel de chaves. Para obter mais informações, veja Principais provedores de armazenamento: Armazenamento do Microsoft Azure. Você também pode persistir o anel de chaves ocalmente 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 chamada dataprotection
no contosokeyvault
tem o identificador de chave https://contosokeyvault.vault.azure.net/keys/dataprotection/
. Forneça ao aplicativo as permissões Get, Unwrap Key e Wrap Key para o cofre de chaves.
As sobrecargas de ProtectKeysWithAzureKeyVault
:
- ProtectKeysWithAzureKeyVault(IDataProtectionBuilder, Uri, TokenCredential) permite o uso de um URI keyIdentifier e um tokenCredential para permitir que o sistema de proteção de dados use o cofre de chaves.
- ProtectKeysWithAzureKeyVault(IDataProtectionBuilder, String, IKeyEncryptionKeyResolver) permite o uso de uma cadeia de caracteres keyIdentifier e IKeyEncryptionKeyResolver para permitir que o sistema de proteção de dados use o cofre de chaves.
Se o aplicativo usar os pacotes mais antigos do Azure (Microsoft.AspNetCore.DataProtection.AzureStorage e Microsoft.AspNetCore.DataProtection.AzureKeyVault), recomendamos remover essas referências e atualizar para Azure.Extensions.AspNetCore.DataProtection.Blobs e Azure.Extensions.AspNetCore.DataProtection.Keys. Esses pacotes são onde novas atualizações são fornecidas e resolvem alguns dos principais problemas de segurança e estabilidade com os pacotes mais antigos.
builder.Services.AddDataProtection()
// This blob must already exist before the application is run
.PersistKeysToAzureBlobStorage("<storageAccountConnectionString", "<containerName>", "<blobName>")
// 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:
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"));
Aviso
Se você alterar o local de persistência de chave, o sistema deixará de criptografar automaticamente as chaves em rest, pois não saberá 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 :
builder.Services.AddDataProtection()
.PersistKeysToDbContext<SampleDbContext>();
O código anterior armazena as chaves no banco de dados configurado. O contexto de banco de dados que está sendo usado deve implementar IDataProtectionKeyContext
. IDataProtectionKeyContext
expõe a propriedade DataProtectionKeys
public DbSet<DataProtectionKey> DataProtectionKeys { get; set; } = null!;
Essa propriedade representa a tabela na qual as chaves são armazenadas. Crie a tabela manualmente ou com Migrações DbContext
. Para obter mais informações, consulte DataProtectionKey.
ProtectKeysWith*
Você pode configurar o sistema para proteger chaves em rest 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 rest com um certificado X.509 específico:
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
.ProtectKeysWithCertificate(builder.Configuration["CertificateThumbprint"]);
Você pode fornecer um X509Certificate2 para ProtectKeysWithCertificate, como um certificado carregado de um arquivo:
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
.ProtectKeysWithCertificate(
new X509Certificate2("certificate.pfx", builder.Configuration["CertificatePassword"]));
Consulte Criptografia de chave em Rest para ver mais exemplos e discussões sobre os mecanismos de criptografia de chave internos.
UnprotectKeysWithAnyCertificate
Você pode girar certificados e descriptografar chaves em rest usando uma matriz de certificados X509Certificate2 com UnprotectKeysWithAnyCertificate:
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
.ProtectKeysWithCertificate(
new X509Certificate2("certificate.pfx", builder.Configuration["CertificatePassword"]))
.UnprotectKeysWithAnyCertificate(
new X509Certificate2("certificate_1.pfx", builder.Configuration["CertificatePassword_1"]),
new X509Certificate2("certificate_2.pfx", builder.Configuration["CertificatePassword_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:
builder.Services.AddDataProtection()
.SetDefaultKeyLifetime(TimeSpan.FromDays(14));
setApplicationName
Por padrão, o sistema de Proteção de Dados isola os aplicativos uns dos outros com base em seus caminhos de raiz de conteúdo , mesmo que compartilhem o mesmo repositório de chaves físicas. Esse isolamento impede que os aplicativos compreendam os conteúdos protegidos uns dos outros.
Para compartilhar cargas protegidas entre aplicativos:
- Configure SetApplicationName em cada aplicativo com o mesmo valor.
- Use a mesma versão da pilha da API de Proteção de Dados nos aplicativos. Execute um dos seguintes procedimentos nos arquivos de projeto dos aplicativos:
- Referencie a mesma versão da estrutura compartilhada por meio do metapacote Microsoft.AspNetCore.App.
- Referencie a mesma versão do pacote de Proteção de Dados .
builder.Services.AddDataProtection()
.SetApplicationName("<sharedApplicationName>");
SetApplicationName define DataProtectionOptions.ApplicationDiscriminator internamente . Para fins de solução de problemas, o valor atribuído ao discriminador pela estrutura pode ser registrado com o seguinte código colocado depois que o WebApplication é interno:Program.cs
var discriminator = app.Services.GetRequiredService<IOptions<DataProtectionOptions>>()
.Value.ApplicationDiscriminator;
app.Logger.LogInformation("ApplicationDiscriminator: {ApplicationDiscriminator}", discriminator);
Para obter mais informações sobre como o discriminador é usado, consulte as seguintes seções mais adiante nesse artigo:
Aviso
No .NET 6, WebApplicationBuilder normaliza o caminho raiz do conteúdo para terminar com um DirectorySeparatorChar. Por exemplo, no Windows, o caminho raiz do conteúdo termina em \
e no Linux /
. Outros hosts não normalizam o caminho. A maioria dos aplicativos que migram de HostBuilder ou WebHostBuilder não compartilham o mesmo nome de aplicativo porque não terão o encerramento DirectorySeparatorChar
. Para contornar esse problema, remova o caractere separador de diretório e defina o nome do aplicativo manualmente, conforme mostrado no código a seguir:
using Microsoft.AspNetCore.DataProtection;
using System.Reflection;
var builder = WebApplication.CreateBuilder(args);
var trimmedContentRootPath = builder.Environment.ContentRootPath.TrimEnd(Path.DirectorySeparatorChar);
builder.Services.AddDataProtection()
.SetApplicationName(trimmedContentRootPath);
var app = builder.Build();
app.MapGet("/", () => Assembly.GetEntryAssembly()!.GetName().Name);
app.Run();
DisableAutomaticKeyGeneration
Você pode ter um cenário em que não deseja que um aplicativo role automaticamente as chaves (criar novas chaves) à medida que elas se aproximam da expiração. Um exemplo desse cenário pode ser aplicativos configurados 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 chaves. Os aplicativos secundários podem ser configurados para tratar o anel de chave como somente leitura configurando o sistema com DisableAutomaticKeyGeneration:
builder.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 uns dos outros, mesmo que esses aplicativos estejam em execução na mesma conta de processo de trabalho e estejam usando o mesmo material de chave mestra. Isso é semelhante ao modificador IsolateApps do elemento system.web <machineKey>
.
O mecanismo de isolamento funciona considerando cada aplicativo no computador local como um locatário exclusivo, portanto, o root IDataProtector para qualquer aplicativo específico inclui automaticamente a ID do aplicativo como um discriminador (ApplicationDiscriminator). 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 computadores no Web farm.
- Para aplicativos auto-hospedados em execução Kestrel no servidor, a ID exclusiva é o caminho físico para o aplicativo no disco.
O identificador exclusivo foi projetado para sobreviver às redefinições, tanto do aplicativo individual quanto do próprio computador.
Esse mecanismo de isolamento pressupõe 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 confiáveis, 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 do DataProtectionProvider
), o isolamento do aplicativo será desabilitado por padrão. Quando o isolamento do aplicativo é desabilitado, todos os aplicativos apoiados pelo mesmo material de chave podem compartilhar cargas, 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 de aplicativo
Considere os seguintes pontos 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 chaves 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 chaves. Ele não espera que permissões de nível de item, como as fornecidas pelo Azure KeyVault, sejam usadas para impor isolamento extra. A tentativa de permissões no nível do item gera erros de aplicativo. Se você não quiser contar com o isolamento interno do aplicativo, os locais de repositório de chaves separados devem ser usados e não compartilhados entre aplicativos.
O aplicativo discriminatório (ApplicationDiscriminator) é usado para permitir que diferentes aplicativos compartilhem o mesmo material de chave mestra, mas para manter suas cargas criptográficas distintas umas das outras. Para que os aplicativos possam ler as cargas criptográficas uns dos outros, eles devem ter o mesmo discriminador de aplicativo, que pode ser definido chamando
SetApplicationName
.Se um aplicativo for comprometido (por exemplo, por um ataque RCE), todos os materiais de chave mestra acessíveis a esse aplicativo também deverão ser considerados comprometidos, independentemente de seu estado de proteção em rest. Isso implica que, se dois aplicativos forem apontados para o mesmo repositório, mesmo que usem discriminadores de aplicativo diferentes, o comprometimento de um deles será funcionalmente equivalente ao comprometimento de ambos.
Essa cláusula "funcionalmente equivalente a um comprometimento de ambos" é mantida mesmo que os dois aplicativos usem mecanismos diferentes para proteção de chave em rest. Normalmente, essa não é uma configuração esperada. O mecanismo de proteção em rest destina-se a fornecer proteção caso um ciberinvasor receba acesso de leitura ao repositório. Um ciberinvasor que obtém acesso de gravação ao repositório (talvez porque alcançou a permissão de execução de código em um aplicativo) pode inserir chaves mal-intencionadas no armazenamento. O sistema de proteção de dados intencionalmente não fornece proteção contra um ciberinvasor que obtém acesso de gravação ao repositório de chaves.
Se os aplicativos precisarem permanecer verdadeiramente isolados uns dos outros, eles deverão usar repositórios de chaves diferentes. Isso naturalmente sai da definição de "isolado". Os aplicativos não serão isolados se todos tiverem acesso de Leitura e Gravação aos armazenamentos de dados uns dos outros.
Alterando algoritmos com UseCryptographicAlgorithms
A pilha de 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:
builder.Services.AddDataProtection()
.UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration
{
EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
});
O EncryptionAlgorithm padrão é AES-256-CBC e o Padrão ValidationAlgorithm é HMACSHA256. A política padrão pode ser definida por um administrador do sistema por meio de uma política de todo o computador, mas uma chamada explícita para UseCryptographicAlgorithms
substituir a política padrão.
Chamar UseCryptographicAlgorithms
permite que você especifique o algoritmo desejado de uma lista interna predefinida. Você não precisa se preocupar com os detalhes de implementação do algoritmo. No cenário acima, o sistema de Proteção de Dados tenta usar a implementação de CNG do AES se estiver em execução no Windows. Caso contrário, ele voltará para a classe gerenciada System.Security.Cryptography.Aes .
Você pode especificar manualmente uma implementação por meio de uma chamada para UseCustomCryptographicAlgorithms.
Dica
A alteração de 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 aponte para os tipos de implementação:
builder.Services.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)
});
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 especialmente case alguns valores, como typeof(Aes)
para 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 do modo CBC com preenchimento PKCS nº 7. O KeyedHashAlgorithm deve ter um tamanho de resumo de >= 128 bits e deve dar suporte a chaves de comprimento iguais ao comprimento de resumo do algoritmo de hash. O KeyedHashAlgorithm não é estritamente necessário para ser HMAC.
Especificando algoritmos CNG personalizados do Windows
Para especificar um algoritmo CNG personalizado do Windows usando a criptografia do modo CBC com validação HMAC, crie uma instância CngCbcAuthenticatedEncryptorConfiguration que contenha as informações algorítmicas:
builder.Services.AddDataProtection()
.UseCustomCryptographicAlgorithms(new CngCbcAuthenticatedEncryptorConfiguration
{
// 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 e um tamanho de bloco de >= 64 bits e deve dar suporte à criptografia do modo CBC com preenchimento PKCS nº 7. 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 *Provider podem ser definidas como nulas para usar o provedor padrão para o algoritmo especificado. Para obter mais informações, consulte a documentação BCryptOpenAlgorithmProvider.
Para especificar um algoritmo CNG personalizado do Windows usando a criptografia do modo Galois/Counter com validação, crie uma instância CngGcmAuthenticatedEncryptorConfiguration que contenha as informações algorítmicas:
builder.Services.AddDataProtection()
.UseCustomCryptographicAlgorithms(new CngGcmAuthenticatedEncryptorConfiguration
{
// 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 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. Para obter mais informações, consulte a documentação BCryptOpenAlgorithmProvider.
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 principais rotinas de criptografia e descriptografia. Para obter mais informações, consulte IAuthenticatedEncryptor em Extensibilidade de criptografia principal.
Chaves persistentes ao hospedar em um contêiner do Docker
Ao hospedar em um contêiner do Docker, as chaves devem ser mantidas em:
- Uma pasta que é um volume do Docker que persiste além do tempo de vida do contêiner, como um volume compartilhado ou um volume montado em host.
- Um provedor externo, como Armazenamento de Blobs do Azure (mostrado na
ProtectKeysWithAzureKeyVault
seção) ou Redis.
Teclas persistentes com Redis
Somente as versões do Redis que dão suporte à Persistência de Dados Redis devem ser usadas para armazenar chaves. O Armazenamento de Blobs do Azure é persistente e pode ser usado para armazenar chaves. Saiba mais neste tópico do GitHub.
Log de DadosProteção
Habilite o registro em log de nível Information
do DataProtection para ajudar no problema de diagnóstico. O arquivo appsettings.json
a seguir habilita o log de informações da API DataProtection:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.AspNetCore.DataProtection": "Information"
}
},
"AllowedHosts": "*"
}
Para obter mais informações sobre registro em log, consulte Registrar em log no .NET Core e no ASP.NET Core.
Recursos adicionais
Quando o sistema de Proteção de Dados é inicializado, ele aplica as configurações padrão com base no ambiente operacional. Essas configurações são apropriadas para aplicativos em execução em um único computador. No entanto, há casos em que um desenvolvedor pode querer alterar as configurações padrão:
- O aplicativo é distribuído em vários computadores.
- 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 rest, mas isso não impede que os invasores criem novas chaves. Consequentemente a segurança do aplicativo é afetada. 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 seu anel de chaves no disco, use permissões do sistema de arquivos. Verifique se apenas a identity na qual seu aplicativo Web é executado tem acesso de leitura, gravação e criação a esse diretório. Se você usar o Armazenamento de Blobs do Azure, somente o aplicativo Web deverá ter a capacidade de ler, gravar ou criar novas entradas no repositório de blobs 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 pacotes NuGet são necessários para as extensões de Proteção de Dados usadas neste artigo:
ProtectKeysWithAzureKeyVault
Entre no Azure usando a CLI, por exemplo:
az login
Para armazenar chaves no Azure Key Vault, configure o sistema com ProtectKeysWithAzureKeyVault na classe Startup
. 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());
}
Para que um aplicativo se comunique e se autorize com o KeyVault, o pacote do AzureIdentity deve ser adicionado.
Defina o local de armazenamento do anel de chaves (por exemplo, PersistKeysToAzureBlobStorage). O local deve ser definido porque a chamada ProtectKeysWithAzureKeyVault
implementa um IXmlEncryptor que desabilita as configurações automáticas de proteção de dados, incluindo o local de armazenamento do anel de chaves. O exemplo anterior usa Armazenamento de Blobs do Azure para persistir o anel de chaves. Para obter mais informações, veja Principais provedores de armazenamento: Armazenamento do Microsoft Azure. Você também pode persistir o anel de chaves ocalmente 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 chamada dataprotection
no contosokeyvault
tem o identificador de chave https://contosokeyvault.vault.azure.net/keys/dataprotection/
. Forneça ao aplicativo as permissões Get, Unwrap Key e Wrap Key para o cofre de chaves.
As sobrecargas de ProtectKeysWithAzureKeyVault
:
- ProtectKeysWithAzureKeyVault(IDataProtectionBuilder, Uri, TokenCredential) permite o uso de um URI keyIdentifier e um tokenCredential para permitir que o sistema de proteção de dados use o cofre de chaves.
- ProtectKeysWithAzureKeyVault(IDataProtectionBuilder, String, IKeyEncryptionKeyResolver) permite o uso de uma cadeia de caracteres keyIdentifier e IKeyEncryptionKeyResolver para permitir que o sistema de proteção de dados use o cofre de chaves.
Se o aplicativo usar os pacotes mais antigos do Azure (Microsoft.AspNetCore.DataProtection.AzureStorage e Microsoft.AspNetCore.DataProtection.AzureKeyVault), recomendamos remover essas referências e atualizar para Azure.Extensions.AspNetCore.DataProtection.Blobs e Azure.Extensions.AspNetCore.DataProtection.Keys. Esses pacotes são onde novas atualizações são fornecidas e resolvem alguns dos principais problemas de segurança e estabilidade com os pacotes mais antigos.
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 deixará de criptografar automaticamente as chaves em rest, pois não saberá 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 de 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 Migrações DbContext
. Para obter mais informações, consulte DataProtectionKey.
ProtectKeysWith*
Você pode configurar o sistema para proteger chaves em rest 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 rest com um certificado X.509 específico:
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
.ProtectKeysWithCertificate(Configuration["Thumbprint"]);
}
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 Rest para ver mais exemplos e discussões sobre os mecanismos de criptografia de chave internos.
UnprotectKeysWithAnyCertificate
Você pode girar certificados e descriptografar chaves em rest 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 os aplicativos uns dos outros com base em seus caminhos de raiz de conteúdo , mesmo que compartilhem o mesmo repositório de chaves físicas. Esse isolamento impede que os aplicativos compreendam os conteúdos protegidos uns dos outros.
Para compartilhar cargas protegidas entre aplicativos:
- Configure SetApplicationName em cada aplicativo com o mesmo valor.
- Use a mesma versão da pilha da API de Proteção de Dados nos aplicativos. Execute um dos seguintes procedimentos nos arquivos de projeto dos aplicativos:
- Referencie a mesma versão da estrutura compartilhada por meio do metapacote Microsoft.AspNetCore.App.
- Referencie a mesma versão do pacote de Proteção de Dados .
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection()
.SetApplicationName("shared app name");
}
SetApplicationName define DataProtectionOptions.ApplicationDiscriminator internamente . Para obter mais informações sobre como o discriminador é usado, consulte as seguintes seções mais adiante nesse artigo:
DisableAutomaticKeyGeneration
Você pode ter um cenário em que não deseja que um aplicativo role automaticamente as chaves (criar novas chaves) à medida que elas se aproximam da expiração. Um exemplo desse cenário pode ser aplicativos configurados 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 chaves. 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 uns dos outros, mesmo que esses aplicativos estejam em execução na mesma conta de processo de trabalho e estejam usando o mesmo material de chave mestra. Isso é semelhante ao modificador IsolateApps do elemento system.web <machineKey>
.
O mecanismo de isolamento funciona considerando cada aplicativo no computador local como um locatário exclusivo, portanto, o root IDataProtector para qualquer aplicativo específico inclui automaticamente a ID do aplicativo como um discriminador (ApplicationDiscriminator). 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 computadores no Web farm.
- Para aplicativos auto-hospedados em execução Kestrel no servidor, a ID exclusiva é o caminho físico para o aplicativo no disco.
O identificador exclusivo foi projetado para sobreviver às redefinições, tanto do aplicativo individual quanto do próprio computador.
Esse mecanismo de isolamento pressupõe 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 confiáveis, 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 do DataProtectionProvider
), o isolamento do aplicativo será desabilitado por padrão. Quando o isolamento do aplicativo é desabilitado, todos os aplicativos apoiados pelo mesmo material de chave podem compartilhar cargas, 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 de aplicativo
Considere os seguintes pontos 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 chaves 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 chaves. Ele não espera que permissões de nível de item, como as fornecidas pelo Azure KeyVault, sejam usadas para impor isolamento extra. A tentativa de permissões no nível do item gera erros de aplicativo. Se você não quiser contar com o isolamento interno do aplicativo, os locais de repositório de chaves separados devem ser usados e não compartilhados entre aplicativos.
O aplicativo discriminatório (ApplicationDiscriminator) é usado para permitir que diferentes aplicativos compartilhem o mesmo material de chave mestra, mas para manter suas cargas criptográficas distintas umas das outras. Para que os aplicativos possam ler as cargas criptográficas uns dos outros, eles devem ter o mesmo discriminador de aplicativo, que pode ser definido chamando
SetApplicationName
.Se um aplicativo for comprometido (por exemplo, por um ataque RCE), todos os materiais de chave mestra acessíveis a esse aplicativo também deverão ser considerados comprometidos, independentemente de seu estado de proteção em rest. Isso implica que, se dois aplicativos forem apontados para o mesmo repositório, mesmo que usem discriminadores de aplicativo diferentes, o comprometimento de um deles será funcionalmente equivalente ao comprometimento de ambos.
Essa cláusula "funcionalmente equivalente a um comprometimento de ambos" é mantida mesmo que os dois aplicativos usem mecanismos diferentes para proteção de chave em rest. Normalmente, essa não é uma configuração esperada. O mecanismo de proteção em rest destina-se a fornecer proteção caso um ciberinvasor receba acesso de leitura ao repositório. Um ciberinvasor que obtém acesso de gravação ao repositório (talvez porque alcançou a permissão de execução de código em um aplicativo) pode inserir chaves mal-intencionadas no armazenamento. O sistema de proteção de dados intencionalmente não fornece proteção contra um ciberinvasor que obtém acesso de gravação ao repositório de chaves.
Se os aplicativos precisarem permanecer verdadeiramente isolados uns dos outros, eles deverão usar repositórios de chaves diferentes. Isso naturalmente sai da definição de "isolado". Os aplicativos não serão isolados se todos tiverem acesso de Leitura e Gravação aos armazenamentos de dados uns dos outros.
Alterando algoritmos com UseCryptographicAlgorithms
A pilha de 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
});
O EncryptionAlgorithm padrão é AES-256-CBC e o Padrão ValidationAlgorithm é HMACSHA256. A política padrão pode ser definida por um administrador do sistema por meio de uma política de todo o computador, mas uma chamada explícita para UseCryptographicAlgorithms
substituir a política padrão.
Chamar UseCryptographicAlgorithms
permite que você especifique o algoritmo desejado de uma lista interna predefinida. Você não precisa se preocupar com os detalhes de implementação do algoritmo. No cenário acima, o sistema de Proteção de Dados tenta usar a implementação de CNG do AES se estiver em execução no Windows. Caso contrário, ele voltará para a classe gerenciada System.Security.Cryptography.Aes .
Você pode especificar manualmente uma implementação por meio de uma chamada para UseCustomCryptographicAlgorithms.
Dica
A alteração de 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 aponte 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)
});
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 especialmente case alguns valores, como typeof(Aes)
para 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 do modo CBC com preenchimento PKCS nº 7. O KeyedHashAlgorithm deve ter um tamanho de resumo de >= 128 bits e deve dar suporte a chaves de comprimento iguais ao comprimento de resumo do algoritmo de hash. O KeyedHashAlgorithm não é estritamente necessário para ser HMAC.
Especificando algoritmos CNG personalizados do Windows
Para especificar um algoritmo CNG personalizado do Windows usando a criptografia do modo CBC com validação HMAC, crie uma instância CngCbcAuthenticatedEncryptorConfiguration que contenha 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
});
Observação
O algoritmo de criptografia de bloco simétrico deve ter um comprimento de chave de >= 128 bits e um tamanho de bloco de >= 64 bits e deve dar suporte à criptografia do modo CBC com preenchimento PKCS nº 7. 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 *Provider podem ser definidas como nulas para usar o provedor padrão para o algoritmo especificado. Para obter mais informações, consulte a documentação BCryptOpenAlgorithmProvider.
Para especificar um algoritmo CNG personalizado do Windows usando a criptografia do modo Galois/Counter com validação, crie uma instância CngGcmAuthenticatedEncryptorConfiguration que contenha as informações algorítmicas:
services.AddDataProtection()
.UseCustomCryptographicAlgorithms(
new CngGcmAuthenticatedEncryptorConfiguration()
{
// 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 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. Para obter mais informações, consulte a documentação BCryptOpenAlgorithmProvider.
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 principais rotinas de criptografia e descriptografia. Para obter mais informações, consulte IAuthenticatedEncryptor em Extensibilidade de criptografia principal.
Chaves persistentes ao hospedar em um contêiner do Docker
Ao hospedar em um contêiner do Docker, as chaves devem ser mantidas em:
- Uma pasta que é um volume do Docker que persiste além do tempo de vida do contêiner, como um volume compartilhado ou um volume montado em host.
- Um provedor externo, como Armazenamento de Blobs do Azure (mostrado na
ProtectKeysWithAzureKeyVault
seção) ou Redis.
Teclas persistentes com Redis
Somente as versões do Redis que dão suporte à Persistência de Dados Redis devem ser usadas para armazenar chaves. O Armazenamento de Blobs do Azure é persistente e pode ser usado para armazenar chaves. Saiba mais neste tópico do GitHub.
Log de DadosProteção
Habilite o registro em log de nível Information
do DataProtection para ajudar no problema de diagnóstico. O arquivo appsettings.json
a seguir 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 Registrar em log no .NET Core e no ASP.NET Core.