Descripción de Always Encrypted
Always Encrypted cifra datos confidenciales, como números de tarjeta de crédito o información de nómina, en las aplicaciones del lado cliente. La base de datos recibe los datos ya cifrados del lado cliente y no contiene las claves de descifrado. Esto significa que la base de datos no tiene ninguna manera de descifrar estos datos. Cuando el cliente requiera los datos, la base de datos devolverá los datos cifrados y, dado que posee las claves de descifrado, el cliente descifrará los datos.
Always Encrypted ofrece las funcionalidades de cifrado del lado cliente a Azure Cosmos DB. Puesto que los datos se cifran en el lado cliente, el servicio Azure Cosmos DB nunca se proporciona con las claves de descifrado ni los datos sin cifrar. El usuario posee, controla y administra las claves de descifrado. Estas claves se almacenan en Azure Key Vault, donde puede aplicar directivas para controlar las claves y los secretos a los que puede acceder cada cliente.
Conceptos de Always Encrypted
Always Encrypted usa claves de cifrado y directivas de descifrado.
Claves de cifrado
Claves de cifrado de datos
Always Encrypted requiere que cree claves de cifrado de datos (DEK) con antelación. Las claves DEK se crean en el lado cliente mediante el SDK de base de datos de Azure Cosmos DB. Estas claves DEK se almacenan en el servicio Azure Cosmos DB. Las claves DEK se definen en el nivel de base de datos para que se puedan compartir entre varios contenedores. Cada clave DEK que crea se puede usar para cifrar una sola propiedad o para cifrar muchas propiedades. Puede tener varias claves DEK por base de datos.
Claves administradas por el cliente
Una clave DEK debe ajustarse mediante una clave administrada por el cliente (CMK) antes de almacenarse en Azure Cosmos DB. Dado que las claves CMK controlan el ajuste y el desajuste de las claves DEK, controlan el acceso a los datos que se han cifrado con estas últimas. El almacenamiento de las claves administradas por el cliente está diseñado como un modelo extensible o de complemento, con una implementación predeterminada que espera que se almacenen en Azure Key Vault. La relación entre estos componentes se muestra en el diagrama siguiente.
Directiva de cifrado
Las directivas de cifrado son especificaciones de nivel de contenedor que describen cómo se deben cifrar las propiedades JSON. Estas directivas son similares a las directivas de indexación de la estructura. En la versión actual, debe crear estas directivas en el momento de la creación del contenedor y no se pueden actualizar una vez creadas.
Para cada propiedad que quiera cifrar, la directiva de cifrado define lo siguiente:
- Ruta de acceso de la propiedad con el formato /property. Actualmente solo se admiten las rutas de acceso de nivel superior y no se admiten las rutas de acceso anidadas como /path/to/property.
- Id. de la clave DEK que se usará al cifrar y descifrar la propiedad.
- Un tipo de cifrado. Puede ser aleatorio o determinista.
- El algoritmo de cifrado que se usará al cifrar la propiedad. El algoritmo especificado puede invalidar el algoritmo definido al crear la clave si son compatibles.
No puede cifrar el identificador ni la clave de partición del contenedor.
Cifrado aleatorio frente a cifrado determinista
Dado que el servicio Azure Cosmos DB puede necesitar admitir algunas funcionalidades de consulta sobre los datos cifrados y no puede evaluar los datos en texto sin formato, Always Encrypted tiene más de un tipo de cifrado. Los tipos de cifrado que admite Always Encrypted son:
Cifrado determinista: siempre genera el mismo valor cifrado para cualquier valor de texto sin formato y configuración de cifrado determinados. El uso del cifrado determinista permite que las consultas realicen filtros de igualdad en propiedades cifradas. Sin embargo, puede permitir a los atacantes averiguar información sobre los valores cifrados mediante el análisis de patrones en la propiedad cifrada. Esto sucede especialmente cuando hay un conjunto pequeño de posibles valores cifrados, como verdadero o falso, o región norte, sur, este u oeste.
Cifrado aleatorio: usa un método que cifra los datos de una manera menos predecible. El cifrado aleatorio es más seguro, pero evita que las consultas se filtren por propiedades cifradas.
Configuración y uso de Always Encrypted con Azure Cosmos DB
La configuración de Always Encrypted será un proceso de varios pasos, desde la configuración de la clave CMK en Azure Key Vault hasta la creación de la clave DEK y, por último, la creación de contenedores con las directivas de cifrado.
Configuración de Azure Key Vault
Antes de crear la clave CMK, debemos crear una nueva instancia de Azure Key Vault o usar una existente para almacenar la clave CMK.
- En Azure Portal, siga las instrucciones para crear una nueva instancia de Azure Key Vault o elija una existente.
- En Claves, cree una clave.
- Una vez creada la clave, busque la versión actual y copie su identificador de clave completo:
https://<my-key-vault>.vault.azure.net/keys/<key>/<version>. Si desea usar siempre la versión más reciente de la clave, simplemente omita la versión de la clave al final del identificador de clave.
Será necesaria una identidad de Microsoft Entra para conceder a la instancia de Azure Key Vault acceso al SDK de Azure Cosmos DB. Una aplicación de Microsoft Entra o una identidad administrada se usa normalmente como proxy entre el código de cliente y la instancia de Azure Key Vault. Para usar una aplicación de AD como proxy, siga estos pasos:
- Cree una nueva aplicación de Microsoft Entra y agregue un secreto de cliente como se describe en este inicio rápido.
- En la instancia de Azure Key Vault, en Directivas de acceso, seleccione + Agregar directiva de acceso y agregue una nueva directiva:
- En Permisos de las claves, seleccione Obtener, Enumerar, Desencapsular clave, Encapsular clave, **Comprobar y Firmar.
- En Seleccionar entidad de seguridad, busque la aplicación de Microsoft Entra que ha creado antes.
Inicializar el SDK
Para utilizar Always Encrypted, debe adjuntar una instancia de EncryptionKeyStoreProvider a su instancia de SDK de Azure Cosmos DB. Este objeto se usa para interactuar con el almacén de claves que hospeda sus CMK. El proveedor de almacén de claves predeterminado para Azure Key Vault se denomina AzureKeyVaultKeyStoreProvider. Para usar AzureKeyVaultKeyStoreProvider, deberá agregar el paquete Microsoft.Data.Encryption.AzureKeyVaultProvider.
Los fragmentos de código siguientes muestran cómo usar la identidad de una aplicación de Microsoft Entra con un secreto de cliente.
var tokenCredential = new ClientSecretCredential(
"<aad-app-tenant-id>", "<aad-app-client-id>", "<aad-app-secret>");
var keyStoreProvider = new AzureKeyVaultKeyStoreProvider(tokenCredential);
var client = new CosmosClient("<connection-string>")
.WithEncryption(keyStoreProvider);
Cree una clave de cifrado de datos
Una vez creada la clave CMK en Azure Key Vault, es el momento de crear la clave DEK en la base de datos primaria. Para crear esta clave DEK, usaremos el método CreateClientEncryptionKeyAsync y pasaremos la siguiente información:
- un identificador de cadena que identificará de forma única la clave en la base de datos y
- el algoritmo de cifrado previsto para utilizar con la clave. Actualmente, solo se admite un algoritmo.
- El identificador de clave de la CMK almacenada en Azure Key Vault. Este parámetro pasa en un objeto genérico
EncryptionKeyWrapMetadatadondenamepuede ser cualquier nombre descriptivo que quiera y elvaluedebe ser el identificador de clave.
Los fragmentos de código siguientes muestran cómo se crea esta clave DEK en .NET.
var database = client.GetDatabase("my-database");
await database.CreateClientEncryptionKeyAsync(
"my-key",
DataEncryptionKeyAlgorithm.AEAD_AES_256_CBC_HMAC_SHA256,
new EncryptionKeyWrapMetadata(
keyStoreProvider.ProviderName,
"akvKey",
"https://<my-key-vault>.vault.azure.net/keys/<key>/<version>"));
Es una buena práctica de seguridad girar las claves CMK con regularidad. También debe girar la clave CMK si sospecha que la clave CMK actual se ha puesto en peligro. Una vez girada la clave CMK, se proporciona ese nuevo identificador de CMK para que el nuevo contenedor de DEK empiece a usarlo. Esta operación no afecta al cifrado de sus datos, sino a la protección de la clave DEK. Revise el siguiente script que vuelva a ajustar la nueva clave CMK a la clave DEK:
await database.RewrapClientEncryptionKeyAsync(
"my-key",
new EncryptionKeyWrapMetadata(
keyStoreProvider.ProviderName,
"akvKey",
" https://<my-key-vault>.vault.azure.net/keys/<new-key>/<version>"));
Creación de un contenedor con directiva de cifrado
Ahora que hemos configurado las claves CMK y DEK, es el momento de crear un nuevo contenedor mediante el SDK de .NET. Los fragmentos de código siguientes muestran cómo se crea el contenedor my-container mediante la clave DEK my-key creada en el ejemplo anterior. Este contenedor tendrá una directiva de dos cifrados en las propiedades property1 y property2, y una clave de partición partition-key. Ambas propiedades usarán la clave DEK my-key, pero una usará el tipo de cifrado determinista, mientras que la otra usará el tipo aleatorio.
var path1 = new ClientEncryptionIncludedPath
{
Path = "/property1",
ClientEncryptionKeyId = "my-key",
EncryptionType = EncryptionType.Deterministic.ToString(),
EncryptionAlgorithm = DataEncryptionKeyAlgorithm.AEAD_AES_256_CBC_HMAC_SHA256.ToString()
};
var path2 = new ClientEncryptionIncludedPath
{
Path = "/property2",
ClientEncryptionKeyId = "my-key",
EncryptionType = EncryptionType.Randomized.ToString(),
EncryptionAlgorithm = DataEncryptionKeyAlgorithm.AEAD_AES_256_CBC_HMAC_SHA256.ToString()
};
await database.DefineContainer("my-container", "/partition-key")
.WithClientEncryptionPolicy()
.WithIncludedPath(path1)
.WithIncludedPath(path2)
.Attach()
.CreateAsync();
Escritura de datos cifrados
Cuando se escribe un documento de Azure Cosmos DB, el SDK evalúa las directivas de cifrado para determinar si es necesario cifrar alguna propiedad y cómo hacerlo. Si es necesario cifrar una propiedad, crea una cadena con base 64 en lugar del texto original.
Cifrado de tipos complejos
- Si la propiedad que se va a cifrar es una matriz JSON, se cifran todas las entradas de la matriz.
- Si la propiedad que se va a cifrar es un objeto JSON, solo se cifran los valores hoja del objeto. Los nombres de subpropiedad intermedios permanecen en formato de texto sin formato.
Lectura de elementos cifrados
Si va a capturar un solo elemento mediante ID y la clave de partición, ejecutar consultas o leer la fuente de cambios, no se requieren pasos adicionales para descifrar las propiedades cifradas. Esto se debe a que el SDK averigua qué propiedades deben descifrarse mediante la búsqueda de la directiva de cifrado.
Consultas de filtro por propiedades cifradas
El método AddParameterAsync pasa el valor del parámetro de consulta utilizado en las consultas que filtran por propiedades cifradas. Este método toma los argumentos siguientes:
- Nombre del parámetro de consulta.
- Valor que se usará en la consulta.
- Ruta de acceso de la propiedad cifrada (como se define en la directiva de cifrado).
Podemos ver un ejemplo en el que se usa AddParameterAsync a continuación:
var queryDefinition = container.CreateQueryDefinition(
"SELECT * FROM c where c.property1 = @Property1");
await queryDefinition.AddParameterAsync(
"@Property1",
1234,
"/property1");
Las propiedades cifradas solo se pueden usar en filtros de igualdad (WHERE c.property = @Value). Cualquier otro uso devolverá resultados de consulta impredecibles e incorrectos.
Lectura de documentos cuando solo se puede descifrar un subconjunto de propiedades
Las distintas propiedades del documento del mismo contenedor pueden usar directivas de cifrado diferentes. Cada directiva puede usar claves CMK diferentes para cifrar las propiedades. Si el cliente tiene acceso a algunas de las claves CMK usadas para descifrar algunas de las propiedades, pero no a otras claves CMK para descifrar otras propiedades, todavía puede consultar parcialmente los documentos con las propiedades que puede descifrar. Solo debe quitar de sus consultas las propiedades para cuyas claves CMK no tiene acceso. Por ejemplo, si property1 se ha cifrado con key1 y property2 se ha cifrado con key2, y la aplicación solo tiene acceso a key1, la consulta debe omitir property2. Esta consulta podría ser parecida a SELECT c.property1, c.property3 FROM c.