Configurar la protección de datos en ASP.NET Core
Cuando se inicializa el sistema de protección de datos, aplica la configuración predeterminada en función del entorno operativo. Por lo general, esta configuración es adecuada para las aplicaciones que se ejecutan en un solo equipo. Hay casos en los que un desarrollador puede querer cambiar la configuración predeterminada:
- La aplicación se distribuye entre varias máquinas.
- Por motivos de cumplimiento.
En estos escenarios, el sistema de protección de datos ofrece una API de configuración completa.
Advertencia
De forma similar a los archivos de configuración, el anillo de claves de protección de datos debe protegerse con los permisos adecuados. Puede optar por cifrar las claves en reposo, pero esto no impide que los atacantes creen nuevas claves. Por lo tanto, la seguridad de la aplicación se afecta. La ubicación de almacenamiento configurada con Protección de datos debe tener su acceso limitado a la propia aplicación, de forma similar a la forma en que protegería los archivos de configuración. Por ejemplo, si decide almacenar el anillo de claves en el disco, use los permisos del sistema de archivos. Asegúrese de que solo la identidad con la que se ejecuta la aplicación web tiene acceso de lectura, escritura y creación a ese directorio. Si usa Azure Blob Storage, solo la aplicación web debe tener la capacidad de leer, escribir o crear nuevas entradas en el almacén de blobs, etc.
El método de extensión AddDataProtection devuelve un objeto IDataProtectionBuilder. IDataProtectionBuilder expone métodos de extensión que puede encadenar para configurar las opciones de protección de datos.
Los siguientes NuGet paquetes son necesarios para las extensiones de protección de datos usadas en este artículo:
ProtectKeysWithAzureKeyVault
Inicie sesión en Azure mediante la CLI, por ejemplo:
az login
Para almacenar claves en Azure Key Vault, configure el sistema con ProtectKeysWithAzureKeyVault en la Startup clase . blobUriWithSasToken es el URI completo donde se debe almacenar el archivo de clave. El URI debe contener el token de SAS como parámetro de cadena de consulta:
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection()
.PersistKeysToAzureBlobStorage(new Uri("<blobUriWithSasToken>"))
.ProtectKeysWithAzureKeyVault(new Uri("<keyIdentifier>"), new DefaultAzureCredential());
}
Establezca la ubicación de almacenamiento del anillo de claves (por ejemplo, PersistKeysToAzureBlobStorage). La ubicación debe establecerse porque la llamada a implementa un ProtectKeysWithAzureKeyVault IXmlEncryptor que deshabilita la configuración de protección automática de datos, incluida la ubicación de almacenamiento del anillo de claves. En el ejemplo anterior se usa Azure Blob Storage para conservar el anillo de claves. Para obtener más información, vea Proveedores de almacenamiento de claves: Azure Storage. También puede conservar el anillo de claves localmente con PersistKeysToFileSystem.
es keyIdentifier el identificador de clave del almacén de claves que se usa para el cifrado de claves. Por ejemplo, una clave creada en el almacén de claves denominado dataprotection en tiene el identificador de clave contosokeyvault https://contosokeyvault.vault.azure.net/keys/dataprotection/ . Proporcione a la aplicación los permisos Desencapsular clave y Encapsular clave en el almacén de claves.
ProtectKeysWithAzureKeyVault Sobrecargas:
- ProtectKeysWithAzureKeyVault(IDataProtectionBuilder, Uri, TokenCredential) permite el uso de un URI keyIdentifier y un tokenCredential para permitir que el sistema de protección de datos use el almacén de claves.
- ProtectKeysWithAzureKeyVault(IDataProtectionBuilder, string, IKeyEncryptionKeyResolver) permite el uso de una cadena keyIdentifier e IKeyEncryptionKeyResolver para permitir que el sistema de protección de datos use el almacén de claves.
Si la aplicación usa los paquetes anteriores de Azure ( y ) y una combinación de Azure Key Vault y Azure Storage para almacenar y proteger claves, se produce si el blob para el almacenamiento de claves no Microsoft.AspNetCore.DataProtection.AzureStorage Microsoft.AspNetCore.DataProtection.AzureKeyVault System.UriFormatException existe. El blob se puede crear manualmente antes de ejecutar la aplicación en el Azure Portal, o bien usar el procedimiento siguiente:
- Quite la llamada a
ProtectKeysWithAzureKeyVaultpara la primera ejecución para crear el blob en su lugar. - Agregue la llamada a para
ProtectKeysWithAzureKeyVaultlas ejecuciones posteriores.
Se recomienda quitar para la primera ejecución, ya que garantiza que el archivo se crea con el esquema y los ProtectKeysWithAzureKeyVault valores adecuados en su lugar.
Se recomienda actualizar a los paquetes Azure.Extensions.AspNetCore.DataProtection.Blobs y Azure.Extensions.AspNetCore.DataProtection.Keys porque la API proporcionada crea automáticamente el blob si no existe.
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 almacenar claves en un recurso compartido UNC en lugar de en la ubicación predeterminada %LOCALAPPDATA%, configure el sistema con PersistKeysToFileSystem:
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"));
}
Advertencia
Si cambia la ubicación de persistencia de la clave, el sistema ya no cifra automáticamente las claves en reposo, ya que no sabe si DPAPI es un mecanismo de cifrado adecuado.
PersistKeysToDbContext
Para almacenar claves en una base de datos mediante EntityFramework, configure el sistema con el paquete Microsoft.AspNetCore.DataProtection.EntityFrameworkCore:
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection()
.PersistKeysToDbContext<DbContext>()
}
El código anterior almacena las claves en la base de datos configurada. El contexto de base de datos que se usa debe implementar IDataProtectionKeyContext . IDataProtectionKeyContext expone la propiedad DataProtectionKeys
public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }
Esta propiedad representa la tabla en la que se almacenan las claves. Cree la tabla manualmente o con DbContext Migraciones. Vea DataProtectionKey para obtener más información.
ProtectKeysWith*
Puede configurar el sistema para proteger las claves en reposo llamando a cualquiera de las API de configuración * ProtectKeysWith. Considere el ejemplo siguiente, que almacena las claves en un recurso compartido UNC y las cifra en reposo con un certificado X.509 específico:
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
.ProtectKeysWithCertificate(Configuration["Thumbprint"]);
}
En ASP.NET Core 2.1 o posterior, puede proporcionar X509Certificate2 a ProtectKeysWithCertificate,como un certificado cargado desde un archivo:
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"))
.ProtectKeysWithCertificate(
new X509Certificate2("certificate.pfx", Configuration["Thumbprint"]));
}
Consulte Cifrado de claves en reposo para obtener más ejemplos y discusión sobre los mecanismos de cifrado de claves integrados.
UnprotectKeysWithAnyCertificate
En ASP.NET Core 2.1 o posterior, puede rotar certificados y descifrar claves en reposo mediante una matriz de certificados X509Certificate2 con 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 el sistema para que use una vigencia de clave de 14 días en lugar de los 90 días predeterminados, use SetDefaultKeyLifetime:
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection()
.SetDefaultKeyLifetime(TimeSpan.FromDays(14));
}
SetApplicationName
De forma predeterminada, el sistema de protección de datos aísla las aplicaciones entre sí en función de sus rutas de acceso raíz de contenido, incluso si comparten el mismo repositorio de claves físicas. Esto impide que las aplicaciones comprendan las cargas protegidas entre sí.
Para compartir cargas protegidas entre aplicaciones:
- Configure SetApplicationName en cada aplicación con el mismo valor.
- Use la misma versión de la pila de la API de protección de datos en todas las aplicaciones. Realice una de las siguientes acciones en los archivos de proyecto de las aplicaciones:
- Haga referencia a la misma versión del marco compartido a través Microsoft.AspNetCore.App metapaquete.
- Haga referencia a la misma versión del paquete de protección de datos.
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection()
.SetApplicationName("shared app name");
}
DisableAutomaticKeyGeneration
Es posible que tenga un escenario en el que no quiera que una aplicación revierte automáticamente las claves (crear nuevas claves) a medida que se aproximan a la expiración. Un ejemplo de esto podrían ser las aplicaciones configuradas en una relación principal/secundaria, donde solo la aplicación principal es responsable de los problemas de administración de claves y las aplicaciones secundarias simplemente tienen una vista de solo lectura del anillo de claves. Las aplicaciones secundarias se pueden configurar para tratar el anillo de claves como de solo lectura mediante la configuración del sistema con DisableAutomaticKeyGeneration :
public void ConfigureServices(IServiceCollection services)
{
services.AddDataProtection()
.DisableAutomaticKeyGeneration();
}
Aislamiento por aplicación
Cuando un host de ASP.NET Core proporciona el sistema de protección de datos, aísla automáticamente las aplicaciones entre sí, incluso si esas aplicaciones se ejecutan en la misma cuenta de proceso de trabajo y usan el mismo material de clave maestra. Esto es algo similar al modificador IsolateApps del elemento de <machineKey> System.Web.
El mecanismo de aislamiento funciona teniendo en cuenta cada aplicación en el equipo local como un inquilino único, por lo que la raíz de cualquier aplicación determinada incluye automáticamente el identificador de aplicación IDataProtector como discriminador. El identificador único de la aplicación es la ruta de acceso física de la aplicación:
- Para las aplicaciones hospedadas en IIS, el identificador único es la ruta de acceso física de IIS de la aplicación. Si una aplicación se implementa en un entorno de granja de servidores web, este valor es estable suponiendo que los entornos de IIS se configuran de forma similar en todas las máquinas de la granja de servidores web.
- En el caso de las aplicaciones auto hospedadas que se ejecutan en el Kestrel servidor, el identificador único es la ruta de acceso física a la aplicación en disco.
El identificador único está diseñado para sobrevivir a los restablecimientos tanto de — la aplicación individual como de la propia máquina.
Este mecanismo de aislamiento supone que las aplicaciones no son malintencionadas. Una aplicación malintencionada siempre puede afectar a cualquier otra aplicación que se ejecute en la misma cuenta de proceso de trabajo. En un entorno de hospedaje compartido en el que las aplicaciones no son de confianza mutua, el proveedor de hospedaje debe tomar medidas para garantizar el aislamiento de nivel de sistema operativo entre las aplicaciones, incluida la separación de los repositorios de claves subyacentes de las aplicaciones.
Si un host de ASP.NET Core no proporciona el sistema de protección de datos (por ejemplo, si crea instancias de él a través del tipo concreto), el aislamiento de la aplicación está DataProtectionProvider deshabilitado de forma predeterminada. Cuando el aislamiento de aplicaciones está deshabilitado, todas las aplicaciones con el mismo material de clave pueden compartir cargas, siempre y cuando proporcionen los propósitos adecuados. Para proporcionar aislamiento de aplicaciones en este entorno, llame al método SetApplicationName en el objeto de configuración y proporcione un nombre único para cada aplicación.
Protección de datos y aislamiento de aplicaciones
Tenga en cuenta lo siguiente para el aislamiento de aplicaciones:
Cuando varias aplicaciones apuntan al mismo repositorio de claves, la intención es que las aplicaciones compartan el mismo material de clave maestra. La protección de datos se desarrolla con la suposición de que todas las aplicaciones que comparten un anillo de claves pueden acceder a todos los elementos de ese anillo de claves. El identificador único de la aplicación se usa para aislar las claves específicas de la aplicación derivadas de las claves proporcionadas por el anillo de claves. No espera que se utilicen permisos de nivel de elemento, como los proporcionados por Azure KeyVault, para aplicar aislamiento adicional. El intento de permisos de nivel de elemento genera errores de aplicación. Si no desea confiar en el aislamiento integrado de la aplicación, se deben usar ubicaciones de almacén de claves independientes y no compartir entre aplicaciones.
El discriminador de aplicación se usa para permitir que distintas aplicaciones compartan el mismo material de clave maestra, pero para mantener sus cargas criptográficas distintas entre sí. Para que las aplicaciones puedan leer las cargas criptográficas entre sí, deben tener el mismo discriminador de aplicación.
Si una aplicación está en peligro (por ejemplo, por un ataque RCE), todo el material de clave maestra accesible para esa aplicación también debe considerarse en peligro, independientemente de su estado de protección en reposo. Esto implica que si dos aplicaciones apuntan al mismo repositorio, incluso si usan discriminadores de aplicaciones diferentes, un riesgo de una es funcionalmente equivalente a un riesgo de ambos.
Esta cláusula "funcionalmente equivalente a un riesgo de ambos" contiene incluso si las dos aplicaciones usan mecanismos diferentes para la protección de claves en reposo. Normalmente, esta no es una configuración esperada. El mecanismo de protección en reposo está pensado para proporcionar protección en caso de que un adversario obtenga acceso de lectura al repositorio. Un adversario que obtiene acceso de escritura al repositorio (quizás porque ha alcanzado el permiso de ejecución de código dentro de una aplicación) puede insertar claves malintencionadas en el almacenamiento. El sistema de protección de datos no proporciona intencionadamente protección contra un adversario que obtiene acceso de escritura al repositorio de claves.
Si las aplicaciones necesitan permanecer realmente aisladas entre sí, deben usar repositorios de claves diferentes. Esto queda fuera de la definición de "aislado". Las aplicaciones no están aisladas si todas tienen acceso de lectura y escritura a los almacenes de datos de los demás.
Cambio de algoritmos con UseCryptographicAlgorithms
La pila de protección de datos permite cambiar el algoritmo predeterminado que usan las claves recién generadas. La manera más sencilla de hacerlo es llamar a UseCryptographicAlgorithms desde la devolución de llamada de configuración:
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
});
El valor predeterminado de EncryptionAlgorithm es AES-256-CBC y el valor predeterminado de ValidationAlgorithm es HMACSHA256. Un administrador del sistema puede establecer la directiva predeterminada através de una directiva de todo el equipo, pero una llamada explícita a invalida la UseCryptographicAlgorithms directiva predeterminada.
Llamar a permite especificar el algoritmo deseado de una lista UseCryptographicAlgorithms integrada predefinida. No es necesario preocuparse por la implementación del algoritmo. En el escenario anterior, el sistema de protección de datos intenta usar la implementación de CNG de AES si se ejecuta en Windows. De lo contrario, vuelve a la clase System.Security.Cryptography.Aes administrada.
Puede especificar manualmente una implementación a través de una llamada a UseCustomCryptographicAlgorithms.
Sugerencia
El cambio de algoritmos no afecta a las claves existentes en el anillo de claves. Solo afecta a las claves recién generadas.
Especificación de algoritmos administrados personalizados
Para especificar algoritmos administrados personalizados, cree una instancia de ManagedAuthenticatedEncryptorConfiguration que seleccione los tipos de implementación:
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 administrados personalizados, cree una instancia de ManagedAuthenticatedEncryptionSettings que apunta a los tipos de implementación:
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)
});
Por lo general, las propiedades Type deben apuntar a implementaciones concretas y con instancias (a través de un * ctor público sin parámetros) de SymmetricAlgorithm y KeyedHashAlgorithm,aunque el sistema realiza casos especiales de algunos valores como por typeof(Aes) comodidad.
Nota
SymmetricAlgorithm debe tener una longitud de clave de ≥ 128 bits y un tamaño de bloque de ≥ 64 bits, y debe admitir el cifrado en modo CBC con PKCS #7 relleno. KeyedHashAlgorithm debe tener un tamaño de resumen de >= 128 bits y debe admitir claves de longitud igual a la longitud de resumen del algoritmo hash. KeyedHashAlgorithm no es estrictamente necesario para ser HMAC.
Especificación de algoritmos Windows CNG personalizados
Para especificar un algoritmo CNG Windows personalizado mediante el cifrado en modo CBC con validación de HMAC, cree una instancia de CngCbcAuthenticatedEncryptorConfiguration que contenga la información algorítmica:
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 un algoritmo CNG Windows personalizado mediante el cifrado en modo CBC con validación HMAC, cree una instancia de CngCbcAuthenticatedEncryptionSettings que contenga la información algorítmica:
services.AddDataProtection()
.UseCustomCryptographicAlgorithms(
new CngCbcAuthenticatedEncryptionSettings()
{
// Passed to BCryptOpenAlgorithmProvider
EncryptionAlgorithm = "AES",
EncryptionAlgorithmProvider = null,
// Specified in bits
EncryptionAlgorithmKeySize = 256,
// Passed to BCryptOpenAlgorithmProvider
HashAlgorithm = "SHA256",
HashAlgorithmProvider = null
});
Nota
El algoritmo de cifrado de bloques simétricos debe tener una longitud de clave de >= 128 bits, un tamaño de bloque de >= 64 bits y debe admitir el cifrado en modo CBC con PKCS #7 relleno. El algoritmo hash debe tener un tamaño de resumen de >= 128 bits y debe admitir la apertura con la marca _ BCRYPT ALG _ HANDLE _ HMAC _ FLAG. Las * propiedades del proveedor se pueden establecer en NULL para usar el proveedor predeterminado para el algoritmo especificado. Consulte la documentación de BCryptOpenAlgorithmProvider para obtener más información.
Para especificar un algoritmo de CNG Windows personalizado mediante el cifrado Galois/Counter Mode con validación, cree una instancia de CngGcmAuthenticatedEncryptorConfiguration que contenga la información algorítmica:
services.AddDataProtection()
.UseCustomCryptographicAlgorithms(
new CngGcmAuthenticatedEncryptorConfiguration()
{
// Passed to BCryptOpenAlgorithmProvider
EncryptionAlgorithm = "AES",
EncryptionAlgorithmProvider = null,
// Specified in bits
EncryptionAlgorithmKeySize = 256
});
Para especificar un algoritmo de CNG Windows personalizado mediante el cifrado Galois/Counter Mode con validación, cree una instancia de CngGcmAuthenticatedEncryptionSettings que contenga la información algorítmica:
services.AddDataProtection()
.UseCustomCryptographicAlgorithms(
new CngGcmAuthenticatedEncryptionSettings()
{
// Passed to BCryptOpenAlgorithmProvider
EncryptionAlgorithm = "AES",
EncryptionAlgorithmProvider = null,
// Specified in bits
EncryptionAlgorithmKeySize = 256
});
Nota
El algoritmo de cifrado de bloque simétrico debe tener una longitud de clave de >= 128 bits, un tamaño de bloque de exactamente 128 bits y debe admitir el cifrado GCM. Puede establecer la propiedad EncryptionAlgorithmProvider en NULL para usar el proveedor predeterminado para el algoritmo especificado. Consulte la documentación de BCryptOpenAlgorithmProvider para obtener más información.
Especificar otros algoritmos personalizados
Aunque no se expone como una API de primera clase, el sistema de protección de datos es lo suficientemente extensible como para permitir especificar casi cualquier tipo de algoritmo. Por ejemplo, es posible mantener todas las claves contenidas en un módulo de seguridad de hardware (HSM) y proporcionar una implementación personalizada de las rutinas de cifrado y descifrado principales. Consulte IAuthenticatedEncryptor en Extensibilidad de criptografía principal para obtener más información.
Conservación de claves al hospedar en un contenedor de Docker
Al hospedar en un contenedor de Docker, las claves se deben mantener en:
- Una carpeta que es un volumen de Docker que persiste más allá de la duración del contenedor, como un volumen compartido o un volumen montado en host.
- Un proveedor externo, como Azure Blob Storage (que se muestra en la
ProtectKeysWithAzureKeyVaultsección) o Redis.
Conservación de claves con Redis
Solo se deben usar versiones de Redis que admitan persistencia de datos de Redis para almacenar claves. Azure Blob Storage es persistente y se puede usar para almacenar claves. Para más información, consulte este problema de GitHub.
Protección de datos de registro
Habilite Information el registro de nivel de DataProtection para ayudar a diagnosticar el problema. La siguiente appsetting.jsen el archivo habilita el registro de información de la API DataProtection:
{
"Logging": {
"LogLevel": {
"Microsoft.AspNetCore.DataProtection": "Information"
}
}
}
Para obtener más información sobre el registro, vea Registro en .NET Core y ASP.NET Core.