Использование шифрования на стороне клиента с Always Encrypted для Azure Cosmos DB

ОБЛАСТЬ ПРИМЕНЕНИЯ: NoSQL

Внимание

В выпуске пакета шифрования 1.0 появились критические изменения. Если вы создали ключи шифрования данных и контейнеры с поддержкой шифрования с предыдущими версиями, после переноса клиентского кода в пакеты 1.0 потребуется повторно создать базы данных и контейнеры.

Always Encrypted — это функция, предназначенная для защиты конфиденциальных данных, таких как кредитные карта номера или национальные или региональные идентификационные номера (например, номера социального страхования США), хранящиеся в Azure Cosmos DB. Функция Always Encrypted позволяет клиентам шифровать конфиденциальные данные в клиентских приложениях, не раскрывая ключи шифрования базе данных.

Always Encrypted предоставляет возможности шифрования на стороне клиента в Azure Cosmos DB. Шифрование данных на стороне клиента может потребоваться в указанных ниже сценариях.

  • Защита конфиденциальных данных с определенными характеристиками конфиденциальности. Always Encrypted позволяет клиентам шифровать конфиденциальные данные в своих приложениях, чтобы не предоставлять данные или ключи шифрования службе Azure Cosmos DB в виде обычного текста.
  • Реализация управления доступом на уровне отдельных свойств. Так как шифрование контролируется ключами, которые принадлежат вам и которыми вы управляете в Azure Key Vault, вы можете применять политики доступа для управления тем, к каким конфиденциальным свойствам есть доступ у каждого клиента.

Основные понятия

В Always Encrypted для Azure Cosmos DB представлен ряд новых концепций, связанных с настройкой шифрования на стороне клиента.

Ключи шифрования

ключи шифрования данных;

При использовании Always Encrypted данные шифруются с помощью ключей шифрования данных (DEK), которые должны быть созданы заранее. Ключи DEK хранятся в службе Azure Cosmos DB и определяются на уровне базы данных, поэтому их можно использовать совместно в нескольких контейнерах. Ключи DEK создаются на стороне клиента с помощью пакета SDK для Azure Cosmos DB.

Вы можете:

  • создать отдельный ключ DEK для каждого шифруемого свойства;
  • использовать один и тот же ключ DEK для шифрования нескольких свойств.

Ключи, управляемые клиентом

Перед сохранением ключей DEK в Azure Cosmos DB они упаковываются с помощью ключа, управляемого клиентом (CMK). Благодаря управлению упаковкой и распаковкой ключей DEK ключи CMK позволяют эффективно контролировать доступ к данным, шифруемым с помощью соответствующих ключей DEK. Хранилище CMK является расширяемым. Реализация по умолчанию предполагает хранение в Azure Key Vault.

Ключи шифрования

Политика шифрования

Как и политика индексирования, политика шифрования представляет собой спецификацию на уровне контейнера, описывающую способ шифрования свойств JSON. Эта политика должна предоставляться при создании контейнера и является неизменяемой. В текущем выпуске политику шифрования изменять нельзя.

Для каждого свойства, которое требуется зашифровать, политика шифрования определяет следующие параметры:

  • путь к свойству в формате /property; в настоящее время поддерживаются только пути верхнего уровня; вложенные пути, например /path/to/property, не поддерживаются;
  • идентификатор ключа DEK, используемого для шифрования и расшифровки свойства;
  • тип шифрования; Он может быть случайным либо детерминированным.
  • алгоритм шифрования свойства. Указанный алгоритм может переопределять алгоритм, заданный при создании ключа, если они совместимы.

Случайное и детерминированное шифрование

Служба Azure Cosmos DB не имеет доступа к свойствам, зашифрованным с помощью Always Encrypted, в виде обычного текста. Однако она поддерживает некоторые возможности запросов к зашифрованным данным в зависимости от типа шифрования, используемого для свойства. Always Encrypted поддерживает следующие два типа шифрования:

  • Метод детерминированного шифрования всегда создает одно и то же зашифрованное значение, исходя из любого текстового значения и конфигурации шифрования. При использовании детерминированного шифрования запросы могут применять фильтры равенства к зашифрованным свойствам. Однако злоумышленники могут получить некоторую информацию о зашифрованном значении, изучив закономерности в зашифрованном свойстве. Это особенно справедливо в случае с небольшим набором возможных зашифрованных значений, например True или False, север, запад, юг или восток.

  • Случайное шифрование использует метод, который шифрует данные менее предсказуемым образом. Случайное шифрование является более безопасным, но не позволяет запросам фильтровать зашифрованные свойства.

Дополнительные сведения о детерминированном и случайном шифровании в Always Encrypted см. в разделе Создание вектора инициализации.

Настройка Azure Key Vault

Приступая к работе с Always Encrypted, сначала нужно создать ключ CMK в Azure Key Vault.

  1. Создайте новый экземпляр Azure Key Vault или выберите существующий.
  2. Создайте ключ в разделе Ключи.
  3. После создания ключа перейдите к его текущей версии и скопируйте его полный идентификатор.
    https://<my-key-vault>.vault.azure.net/keys/<key>/<version>. Если не указать версию ключа в конце идентификатора, используется последняя версия ключа.

Далее необходимо настроить способ доступа пакета SDK для Azure Cosmos DB к экземпляру Azure Key Vault. Эта проверка подлинности выполняется с помощью удостоверения Microsoft Entra. Скорее всего, вы будете использовать удостоверение приложения Microsoft Entra или управляемого удостоверения в качестве прокси-сервера между клиентским кодом и экземпляром Azure Key Vault, хотя любое удостоверение можно использовать. Чтобы использовать удостоверение Microsoft Entra в качестве прокси-сервера, выполните следующие действия.

  1. В экземпляре Azure Key Vault перейдите к разделу Политики доступа и добавьте новую политику.

    1. В разделе Разрешения ключей выберите разрешения Получение, Список, Распаковка ключа, Упаковка ключа, Проверка и Подписывание.
    2. В разделе "Выбор субъекта" найдите удостоверение Microsoft Entra.

Защита ключа CMK от случайного удаления

Чтобы не утратить доступ к зашифрованным данным после случайного удаления ключа CMK, рекомендуется задать два свойства в экземпляре Azure Key Vault: Обратимое удаление и Защита от очистки.

Если вы создаете новый экземпляр Azure Key Vault, включите эти свойства во время создания:

Снимок экрана: обратимое удаление и очистка свойств защиты для нового экземпляра Azure Key Vault.

Если вы используете имеющийся экземпляр Azure Key Vault, проверьте, включены ли эти свойства, на портале Azure в разделе Свойства. Если какое-либо из этих свойств не включено, см. разделы "Включение обратимого удаления" и "Включение защиты от очистки" в одной из следующих статей:

Инициализация пакета SDK

Примечание.

Решение Always Encrypted для Azure Cosmos DB в настоящее время поддерживается:

Чтобы использовать Always Encrypted, необходимо подключить экземпляр KeyResolver к экземпляру пакета SDK для Azure Cosmos DB. Этот класс, определенный в пространстве имен Azure.Security.KeyVault.Keys.Cryptography, используется для взаимодействия с хранилищем ключей, в котором размещаются ключи CMK.

Следующие фрагменты кода используют DefaultAzureCredential класс для получения удостоверения Microsoft Entra, используемого при доступе к экземпляру Azure Key Vault. Примеры создания различных классов TokenCredential можно найти здесь.

Примечание.

Для доступа к классам TokenCredential вам потребуется дополнительный пакет Azure.Identity.

var tokenCredential = new DefaultAzureCredential();
var keyResolver = new KeyResolver(tokenCredential);
var client = new CosmosClient("<connection-string>")
    .WithEncryption(keyResolver, KeyEncryptionKeyResolverName.AzureKeyVault);

Создание ключа шифрования данных

Прежде чем можно будет шифровать данные в контейнере, необходимо создать ключ шифрования данных в родительской базе данных.

Создание ключа шифрования данных осуществляется путем вызова метода CreateClientEncryptionKeyAsync и передачи следующих элементов:

  • строковый идентификатор, который однозначно идентифицирует ключ в базе данных;
  • алгоритм шифрования, который должен использоваться с ключом; в настоящее время можно указать только один алгоритм;
  • идентификатор ключа CMK, хранящегося в Azure Key Vault. Этот параметр передается в универсальный EncryptionKeyWrapMetadata объект, в котором:
    • type определяет тип сопоставителя ключей (например, Azure Key Vault);
    • name может быть любым понятным именем;
    • value должен быть идентификатором ключа;

    Внимание

    После создания ключа перейдите к его текущей версии и скопируйте полный идентификатор ключа: https://<my-key-vault>.vault.azure.net/keys/<key>/<version>. Если не указать версию ключа в конце идентификатора, используется последняя версия ключа.

    • algorithm определяет, какой алгоритм должен использоваться для заключения в оболочку ключа шифрования ключей с помощью ключа, управляемого клиентом.
var database = client.GetDatabase("my-database");
await database.CreateClientEncryptionKeyAsync(
    "my-key",
    DataEncryptionAlgorithm.AeadAes256CbcHmacSha256,
    new EncryptionKeyWrapMetadata(
        KeyEncryptionKeyResolverName.AzureKeyVault,
        "akvKey",
        "https://<my-key-vault>.vault.azure.net/keys/<key>/<version>",
        EncryptionAlgorithm.RsaOaep.ToString()));

Создание контейнера с политикой шифрования

Политика шифрования на уровне контейнера указывается при создании контейнера.

var path1 = new ClientEncryptionIncludedPath
{
    Path = "/property1",
    ClientEncryptionKeyId = "my-key",
    EncryptionType = EncryptionType.Deterministic.ToString(),
    EncryptionAlgorithm = DataEncryptionAlgorithm.AeadAes256CbcHmacSha256
};
var path2 = new ClientEncryptionIncludedPath
{
    Path = "/property2",
    ClientEncryptionKeyId = "my-key",
    EncryptionType = EncryptionType.Randomized.ToString(),
    EncryptionAlgorithm = DataEncryptionAlgorithm.AeadAes256CbcHmacSha256
};
await database.DefineContainer("my-container", "/partition-key")
    .WithClientEncryptionPolicy()
    .WithIncludedPath(path1)
    .WithIncludedPath(path2)
    .Attach()
    .CreateAsync();

Чтение и запись зашифрованных данных

Шифрование данных

Когда документ записывается в Azure Cosmos DB, пакет SDK обращается к политике шифрования, чтобы определить, какие свойства необходимо зашифровать и каким образом. Результатом шифрования является строка в кодировке Base64.

Шифрование сложных типов:

  • Если шифруемое свойство является массивом JSON, шифруется каждый элемент массива.

  • Если шифруемое свойство является объектом JSON, шифруются только листовые узлы объекта. Имена промежуточных подсвойств остаются в виде обычного текста.

Чтение зашифрованных элементов

Для расшифровки зашифрованных свойств при выполнении операций чтения точки (получении одного элемента по его идентификатору и ключу секции), запросов или чтении канала изменений специального действия не требуется. Основанием для этого является

  • Пакет SDK обращается к политике шифрования, чтобы определить, какие свойства необходимо расшифровать.
  • В результат шифрования внедрен исходный тип JSON значения.

Обратите внимание, что разрешение зашифрованных свойств и их последующая расшифровка основаны только на результатах, возвращенных запросами. Например, если свойство property1 зашифровано, но проецируется в property2 (SELECT property1 AS property2 FROM c), оно не будет распознано как зашифрованное свойство при получении пакетом SDK.

Запросы фильтрации к зашифрованным свойствам

При написании запросов, которые фильтруют зашифрованные свойства, для передачи значения параметра запроса необходимо использовать определенный метод. Он принимает следующие аргументы:

  • Имя параметра запроса.
  • значение, используемое в запросе;
  • путь к зашифрованному свойству (определенный в политике шифрования).

Внимание

Зашифрованные свойства можно использовать только в фильтрах равенства (WHERE c.property = @Value). В иных случаях будут возвращаться непредсказуемые и неверные результаты. Это ограничение будет реализовано более строго в следующих версиях пакета SDK.

var queryDefinition = container.CreateQueryDefinition(
    "SELECT * FROM c where c.property1 = @Property1");
await queryDefinition.AddParameterAsync(
    "@Property1",
    1234,
    "/property1");

Чтение документов, в которых можно расшифровать только подмножество свойств

Когда клиент имеет доступ не ко всем ключам CMK, используемым для шифрования свойств, при чтении данных можно расшифровать только подмножество свойств. Например, если свойство property1 зашифровано с помощью ключа key1, а property2 — с помощью ключа key2, клиентское приложение, имеющее доступ только к key1, может прочитать данные, но не property2. В этом случае необходимо считывать данные с помощью SQL-запросов и удалять свойства, которые клиент не может расшифровать: SELECT c.property1, c.property3 FROM c.

Смена ключей CMK

Вы можете сменить ключ CMK (то есть использовать новый ключ CMK вместо текущего), если подозреваете, что текущий ключ CMK был скомпрометирован. Кроме того, регулярная смена ключей CMK — рекомендуемая практика безопасности. Чтобы выполнить смену, достаточно предоставить идентификатор нового ключа CMK, который должен использоваться для упаковки определенного ключа DEK. Обратите внимание, что эта операция не влияет на шифрование данных. Она касается лишь защиты ключей DEK. Доступ к предыдущему ключу CMK не следует отзывать, пока смена не завершится.

await database.RewrapClientEncryptionKeyAsync(
    "my-key",
    new EncryptionKeyWrapMetadata(
        KeyEncryptionKeyResolverName.AzureKeyVault,
        "akvKey",
        "https://<my-key-vault>.vault.azure.net/keys/<new-key>/<version>",
        EncryptionAlgorithm.RsaOaep.ToString()));

Смена ключей DEK

Процесс смены ключа шифрования данных не предлагается в виде готовой к использованию возможности. Это связано с тем, что для обновления DEK требуется проверка всех контейнеров, в которых используется этот ключ, и повторное шифрование всех свойств, зашифрованных с помощью этого ключа. Эта операция может выполняться только на стороне клиента, так как служба Azure Cosmos DB не сохраняет текстовое значение DEK и никогда обращается к нему.

На практике сменить ключ DEK можно путем переноса данных из затронутых контейнеров в новые. Новые контейнеры можно создавать точно так же, как и исходные. Для упрощения переноса данных можно воспользоваться автономным средством миграции на GitHub.

Добавление дополнительных зашифрованных свойств

Добавление дополнительных зашифрованных свойств в существующую политику шифрования не поддерживается по тем же причинам, что и в разделе выше. Для выполнения этой операции требуется полная проверка контейнера, гарантирующая, что все экземпляры свойств зашифрованы должным образом. Эта операция возможна только на стороне клиента. Как и смена ключа DEK, добавление дополнительных зашифрованных свойств выполняется путем переноса данных в новый контейнер с соответствующей политикой шифрования.

Если вы можете гибко добавлять новые зашифрованные свойства с точки зрения схемы, можно также использовать независимую от схемы природу Azure Cosmos DB. Если вы используете свойство, определенное в политике шифрования как "контейнер свойств", можно добавить дополнительные свойства без ограничений. Например, предположим, что свойство property1 определено в политике шифрования, и вы изначально пишете property1.property2 в документах. Если на более позднем этапе необходимо добавить property3 как зашифрованное свойство, вы можете начать писать property1.property3 в документах, и новое свойство будет автоматически зашифровано. Этот подход не требует переноса данных.

Следующие шаги