Proveedores de almacenamiento de claves en ASP.NET Core

El sistema de protección de datos emplea un mecanismo de detección de forma predeterminada para determinar dónde se deben conservar las claves criptográficas. El desarrollador puede invalidar el mecanismo de detección predeterminado y especificar manualmente la ubicación.

Advertencia

Si especifica una ubicación de persistencia de clave explícita, el sistema de protección de datos anula el registro del cifrado de claves predeterminado en reposo, por lo que las claves ya no se cifran en reposo. Se recomienda especificar además un mecanismo de cifrado de clave explícito para las implementaciones de producción.

Sistema de archivos

Para configurar un repositorio de claves basado en el sistema de archivos, llame a la rutina de configuración de PersistKeysToFileSystem como se muestra a continuación. Proporcione un DirectoryInfo que apunte al repositorio donde se deben almacenar las claves:

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

DirectoryInfo puede apuntar a un directorio en el equipo local o a una carpeta de un recurso compartido de red. Si apunta a un directorio en el equipo local (y el escenario es que solo las aplicaciones de la máquina local requieren acceso para usar este repositorio), considere la posibilidad de usar Windows DPAPI (en Windows) para cifrar las claves en reposo. De lo contrario, considere la posibilidad de usar un certificado X.509 para cifrar las claves en reposo.

Azure Storage

El paquete Azure.Extensions.AspNetCore.DataProtection.Blobs permite almacenar claves de protección de datos en Azure Blob Storage. Las claves se pueden compartir entre varias instancias de una aplicación web. Las aplicaciones pueden compartir cookies de autenticación o protección CSRF entre varios servidores.

Para configurar el proveedor de Azure Blob Storage, llame a una de las sobrecargas de PersistKeysToAzureBlobStorage.

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

Si la aplicación web se ejecuta como un servicio de Azure, se puede usar la cadena de conexión para autenticarse en Azure Storage mediante 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);

Nota

La cadena de conexión a la cuenta de almacenamiento se puede encontrar en Azure Portal en la sección "Claves de acceso" o mediante la ejecución del siguiente comando de la CLI:

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

Redis

El paquete Microsoft.AspNetCore.DataProtection.StackExchangeRedis permite almacenar claves de protección de datos en una caché de Redis. Las claves se pueden compartir entre varias instancias de una aplicación web. Las aplicaciones pueden compartir cookies de autenticación o protección CSRF entre varios servidores.

El paquete Microsoft.AspNetCore.DataProtection.Redis permite almacenar claves de protección de datos en una caché de Redis. Las claves se pueden compartir entre varias instancias de una aplicación web. Las aplicaciones pueden compartir cookies de autenticación o protección CSRF entre varios servidores.

Para configurar en Redis, llame a una de las sobrecargas de PersistKeysToStackExchangeRedis:

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

Para configurar en Redis, llame a una de las sobrecargas de PersistKeysToRedis:

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

Para obtener más información, vea los temas siguientes:

Registro

Solo se aplica a las implementaciones de Windows.

A veces, es posible que la aplicación no tenga acceso de escritura al sistema de archivos. Considere un escenario en el que una aplicación se ejecuta como una cuenta de servicio virtual (como la identidad del grupo de aplicaciones de w3wp.exe). En estos casos, el administrador puede aprovisionar una clave del Registro a la que pueda acceder la identidad de la cuenta de servicio. Llame al método de extensión PersistKeysToRegistry como se muestra a continuación. Proporcione un RegistryKey que apunte a la ubicación donde se deben almacenar las claves criptográficas:

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

Importante

Se recomienda usar Windows DPAPI para cifrar las claves en reposo.

Entity Framework Core

El paquete Microsoft.AspNetCore.DataProtection.EntityFrameworkCore proporciona un mecanismo para almacenar claves de protección de datos en una base de datos mediante Entity Framework Core. El paquete NuGet de Microsoft.AspNetCore.DataProtection.EntityFrameworkCore debe agregarse al archivo del proyecto, no forma parte del metapaquete de Microsoft.AspNetCore.App.

Con este paquete, las claves se pueden compartir entre varias instancias de una aplicación web.

Para configurar el proveedor de EF Core, llame al 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);
}

Si quiere que los comentarios de código se traduzcan en más idiomas además del inglés, háganoslo saber en este problema de debate de GitHub.

El parámetro genérico, TContext, debe heredar 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; }
    }
}

Cree la tabla DataProtectionKeys.

Ejecute los siguientes comandos en la ventana consola del administrador de paquetes (PMC):

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

MyKeysContext es el DbContext definido en el ejemplo de código anterior. Si usa un DbContext con un nombre diferente, sustituya el nombre DbContext por MyKeysContext.

La entidad o clase de DataProtectionKeys adopta la estructura que se muestra en la tabla siguiente.

Propiedad o campo Tipo CLR Tipo de SQL
Id int int, PK, IDENTITY(1,1), no es null
FriendlyName string nvarchar(MAX), null
Xml string nvarchar(MAX), null

Repositorio de claves personalizado

Si los mecanismos de la bandeja de entrada no son apropiados, el desarrollador puede especificar su propio mecanismo de persistencia de claves proporcionando un IXmlRepository personalizado.