Always Encrypted について理解する

完了

Always Encrypted により、クライアント側アプリケーション内のクレジット カード番号や給与情報などの機密データが暗号化されます。 データベースは、既に暗号化されているデータをクライアント側から受け取り、暗号化解除キーを含みません。 これは、データベースでこのデータを暗号化解除する方法がないことを意味します。 クライアントでデータが必要になると、データベースから暗号化されたデータが送り返されます。クライアントは暗号化解除キーを保有するため、データの暗号化を解除できます。

Azure Cosmos DB では、Always Encrypted によってクライアントサイド暗号化を実行できます。 データはクライアント側で暗号化されるため、Azure Cosmos DB サービスに暗号化解除キーまたは暗号化されていないデータが提供されることはありません。 暗号化解除キーを所有、制御、および管理するのはお客様です。 これらのキーは Azure Key Vault に保存されます。ここではポリシーを適用して、各クライアントがアクセスできるキーとシークレットを制御できます。

Always Encrypted の概念

Always Encrypted では、暗号化キーと暗号化解除ポリシーが使用されます。

暗号化キー

データ暗号化キー

Always Encrypted では、事前にデータ暗号化キー (DEK) を作成する必要があります。 DEK は、Azure Comsos DB SDK を使用してクライアント側で作成します。 これらの DEK は、Azure Cosmos DB サービスに保存されます。 DEK はデータベース レベルで定義されているため、複数のコンテナー間で共有できます。 作成する各 DEK は、1 つのプロパティのみを暗号化するために使用することも、複数のプロパティを暗号化するために使用することもできます。 データベースごとに複数の DEK を設定することができます。

カスタマー マネージド キー

DEK は、Azure Cosmos DB に保存する前に、カスタマー マネージド キー (CMK) でラップする必要があります。 CMK は DEK のラップとラップ解除を制御するため、これらの DEK で暗号化されたデータへのアクセスを制御します。 CMK の保存には拡張可能な、プラグイン型のモデルを採用しており、既定の実装では Azure Key Vault に保存します。 これらのコンポーネント間の関係を次の図に示します。

常に暗号化された暗号化キーと、Azure Cosmos DB との接続方法を示す図。

暗号化ポリシー

暗号化ポリシーは、JSON プロパティの暗号化方法を記述するコンテナーレベルの仕様です。 これらのポリシーは、構造のインデックス作成ポリシーに似ています。 現在のリリースでは、これらのポリシーをコンテナーの作成時に作成する必要があり、作成後に更新することはできません。

暗号化ポリシーでは、暗号化するプロパティごとに次のものを指定します。

  • /property の形式のプロパティのパス。 現在サポートされているのは最上位のパスのみです。 /path/to/property などの入れ子になったパスはサポートされていません。
  • プロパティを暗号化および復号化するときに使用する DEKID
  • 暗号化の種類。 "ランダム化された (randomized)" タイプまたは "決定論的な (deterministic)" タイプのいずれかです。
  • プロパティの暗号化に使用する暗号化アルゴリズム。 指定したアルゴリズムでキーの作成時に定義されたアルゴリズムをオーバーライドできます (両者に互換性がある場合)。

ID またはコンテナーのパーティション キーを暗号化することはできません。

ランダム化された暗号化と確定的な暗号化

Azure Cosmos DB サービスでは、場合によっては暗号化されたデータに対していくつかの照会機能をサポートする必要があるほか、プレーンテキストのデータを評価できないため、Always Encrypted に複数の暗号化の種類が用意されています。 Always Encrypted でサポートされている暗号化の種類は次のとおりです。

  • 決定論的な暗号化: 特定のプレーン テキスト値と暗号化構成に対して、常に同じ暗号化値が生成されます。 決定論的な暗号化を使用すると、暗号化されたプロパティに対してクエリで等価フィルターを実行できます。 ただし、暗号化されたプロパティのパターンを調べることで、暗号化された情報を攻撃者に推測されることがあり得ます。 暗号化される値の候補が少ない場合、たとえば True/False や North/South/East/West などの文脈では、これが特に問題になります。

  • ランダム化された暗号化: 予測不可能な方法でデータを暗号化する方法を使用します。 ランダム暗号化は安全性が高いですが、クエリによって、暗号化されたプロパティにフィルターをかけることができません。

Azure Cosmos DB での Always Encrypted の設定と使用

Always Encrypted の設定プロセスは、Azure Key Vault での CMK の設定から、DEK の作成、最終的に暗号化ポリシーを使用したコンテナーの作成までの複数のステップから構成されます。

Azure Key Vault をセットアップする

CMK を作成する前に、CMK を保存するための新しい Azure キー コンテナーを作成するか、既存のものを使用する必要があります。

  1. Azure portal で、手順に従って新しい Azure キー コンテナーを作成するか、既存のものを選択します。
  2. [ キー] で、新しいキーを作成します。
  3. キーが作成された後、現在のバージョンを参照し、完全なキー識別子 https://<my-key-vault>.vault.azure.net/keys/<key>/<version> をコピーします。 常に最新バージョンのキーを使用する場合は、キー識別子の末尾にあるキー バージョンを省略してください。

Azure Cosmos DB SDK に Azure Key Vault インスタンスへのアクセスを許可するには、Microsoft Entra ID が必要です。 Microsoft Entra アプリケーションまたはマネージド ID は、一般的に、クライアント コードと Azure Key Vault インスタンス間のプロキシとして使用されます。 AD アプリケーションをプロキシとして使用するには、これらの手順を使用します。

  1. この クイック スタートで説明されているように、新しい Microsoft Entra アプリケーションを作成し、クライアント シークレットを追加します。
  2. Azure Key Vault インスタンスの [アクセス ポリシー] で、[ + アクセス ポリシーの追加] を選択し、新しいポリシーを追加します。
    1. [ キーのアクセス許可] で、[ 取得]、[ リスト]、[ キーのラップ解除]、[ キーの折り返し]、[**確認]、[ 署名] の順に選択します。
    2. [ プリンシパルの選択] で、前に作成した Microsoft Entra アプリケーションを検索します。

SDK を初期化する

Always Encrypted を使用するには、EncryptionKeyStoreProvider のインスタンスを Azure Cosmos DB SDK のインスタンスにアタッチする必要があります。 このオブジェクトを使用して、CMK を保存しているキー ストアを操作します。 Azure Key Vault の既定のキー ストア プロバイダーは AzureKeyVaultKeyStoreProvider という名前です。 AzureKeyVaultKeyStoreProvider を使用するには、Microsoft.Data.Encryption.AzureKeyVaultProvider パッケージを追加する必要があります。

次のスニペットは、Microsoft Entra アプリケーションの ID をクライアント シークレットと共に使用する方法を示しています。

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);

データ暗号化キーを作成する

Azure Key Vault に CMK を作成したら、親データベースに DEK を作成します。 この DEK を作成するには、CreateClientEncryptionKeyAsync メソッドを使用して、次の情報を渡します。

  • データベースにあるキーを一意に特定する文字列識別子。
  • キーと共に使用する暗号化アルゴリズム。 現在サポートしているアルゴリズムは 1 つだけです。
  • Azure Key Vault に保存している CMK のキー識別子。 このパラメーターは通常のオブジェクト EncryptionKeyWrapMetadata に渡します。ここで、name には好みの名前を指定でき、value にはキー識別子を指定する必要があります。

次のスニペットは、.NET でこの DEK を作成する方法を示しています。

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>"));

CMK を定期的にローテーションすることは、セキュリティ上好ましい慣例です。 また、現在の CMK が侵害されたと思われる場合も、CMK をローテーションする必要があります。 CMK がローテーションされたら、新しい CMK 識別子を提供して、DEK 再ラッパーがその使用を開始できるようにします。 この操作は、データの暗号化には影響しませんが、DEK の保護には影響します。 次のスクリプトを見てみましょう。ここでは新しい CMK を DEK に再ラップしています。

await database.RewrapClientEncryptionKeyAsync(
    "my-key",
    new EncryptionKeyWrapMetadata(
        keyStoreProvider.ProviderName,
        "akvKey",
        " https://<my-key-vault>.vault.azure.net/keys/<new-key>/<version>"));

暗号化ポリシーを使用してコンテナーを作成する

CMK と DEK の両方を構成できたら、.NET SDK を使用して新しいコンテナーを作成します。 次のスニペットは、前の例で作成した DEK my-container を使用してコンテナー my-key を作成する方法を示しています。 このコンテナーには、property1property2 の 2 つのプロパティの暗号化ポリシー、およびパーティション キー partition-key があります。 どちらのプロパティでも my-key DEK が使用されていますが、一方では "決定論的な (deterministic)" 暗号化タイプ、もう一方では "ランダム化された (randomized)" 暗号化タイプが使用されています。

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();

暗号化されたデータを書き込む

Azure Cosmos DB ドキュメントを書き込む際、SDK は暗号化ポリシーを評価して、プロパティを暗号化する必要があるかどうか、およびそれらを暗号化する方法を判定します。 プロパティを暗号化する必要がある場合は、元のテキストの代わりに base 64 文字列を作成します。

複合型の暗号化

  • 暗号化するプロパティが JSON 配列であるときは、配列のすべての項目が暗号化されます。
  • 暗号化するプロパティが JSON オブジェクトであるときは、オブジェクトのリーフの値だけが暗号化されます。 中間のサブプロパティ名は、プレーンテキスト形式で保持されます。

暗号化された項目を読む

ID とパーティション キーで単一の項目をフェッチする場合、クエリを実行する場合、または変更フィードを読み取る場合は、暗号化されたプロパティの暗号化を解除するために追加の手順は必要ありません。 これは、暗号化ポリシーを調べることによって、暗号化を解除する必要があるプロパティを SDK が判断するためです。

暗号化されたプロパティにフィルターをかけるクエリ

AddParameterAsync メソッドは、暗号化されたプロパティをフィルター処理するクエリに使用されるクエリ パラメーターの値を渡します。 このメソッドは次の引数を取ります。

  • クエリ パラメーターの名前。
  • クエリで使用する値。
  • 暗号化されたプロパティのパス (暗号化ポリシーで指定)。

AddParameterAsync を使用した例を次に示します。

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

暗号化されたプロパティは等価性のフィルター (WHERE c.property = @Value) でのみ使用できます。 それ以外の方法で使用すると、予測不能で誤ったクエリの結果が返されます。

一部のプロパティだけが解読できる状態でドキュメントを読む

同じコンテナー内の異なるドキュメント プロパティで異なる暗号化ポリシーを使用できます。 各ポリシーで異なる CMK を使用してプロパティを暗号化できます。 クライアントが特定のプロパティを暗号化解除するために使用される特定の CMK にアクセスできる一方で、他のプロパティを暗号化解除するための他の CMK にアクセスできない場合でも、暗号化解除できるプロパティを使用してドキュメントを部分的に照会することができます。 CMK にアクセスできないこれらのプロパティは、クエリから削除する必要があります。 たとえば、property1key1 で暗号化され、property2key2 で暗号化されているときに、アプリが key1 にしかアクセスできない場合は、クエリで property2 を無視する必要があります。 このクエリは、SELECT c.property1, c.property3 FROM c のようになります。