最新の安定バージョンについては、Spring Data MongoDB 5.0.0 を使用してください!

暗号化

クライアント側暗号化は、アプリケーション内のデータを MongoDB に送信する前に暗号化する機能です。Spring Data による暗号化の適用を続行する前に、理想的には MongoDB ドキュメント (英語) から概念を理解し、その機能と制限について詳しく学ぶことをお勧めします。

クライアント側の暗号化を使用するようにドライバー com.mongodb.AutoEncryptionSettings を設定してください。MongoDB は、すべてのフィールド型の暗号化をサポートしているわけではありません。特定のデータ型では、等価比較機能を保持するために決定論的な暗号化が必要です。

クライアント側フィールドレベルの暗号化 (CSFLE)

CSFLE を選択すると、完全な柔軟性が得られ、単一のフィールドに異なるキーを使用できるようになります (たとえば、テナントごとに 1 つのキーのシナリオなど)。
読み進める前に必ず MongoDB CSFLE ドキュメント (英語) を参照してください。

自動暗号化 (CSFLE)

MongoDB は、自動暗号化機能を備えた MongoDB ドライバーを使用して、すぐに使えるクライアント側のフィールドレベル暗号化 (英語) をサポートします。自動暗号化には、明示的な暗号化 / 復号化の手順を行わなくても、暗号化された読み取りおよび書き込み操作を実行できる JSON スキーマが必要です。

暗号化情報を保持する JSON スキーマの定義の詳細については、JSON スキーマセクションを参照してください。

MongoJsonSchema を利用するには、AutoEncryptionSettings と組み合わせる必要があります。MongoClientSettingsBuilderCustomizer 経由。

@Bean
MongoClientSettingsBuilderCustomizer customizer(MappingContext mappingContext) {
    return (builder) -> {

        // ... keyVaultCollection, kmsProvider, ...

        MongoJsonSchemaCreator schemaCreator = MongoJsonSchemaCreator.create(mappingContext);
        MongoJsonSchema patientSchema = schemaCreator
            .filter(MongoJsonSchemaCreator.encryptedOnly())
            .createSchemaFor(Patient.class);

        AutoEncryptionSettings autoEncryptionSettings = AutoEncryptionSettings.builder()
            .keyVaultNamespace(keyVaultCollection)
            .kmsProviders(kmsProviders)
            .extraOptions(extraOpts)
            .schemaMap(Collections.singletonMap("db.patient", patientSchema.schemaDocument().toBsonDocument()))
            .build();

        builder.autoEncryptionSettings(autoEncryptionSettings);
    };
}

明示的な暗号化 (CSFLE)

明示的な暗号化では、MongoDB ドライバーの暗号化ライブラリ (org.mongodb:mongodb-crypt) を使用して暗号化タスクと復号化タスクを実行します。@ExplicitEncrypted アノテーションは、JSON スキーマの作成プロパティコンバーターに使用される @Encrypted アノテーションを組み合わせたものです。言い換えれば、@ExplicitEncrypted は既存のビルドブロックを使用して結合し、簡略化された明示的な暗号化サポートを実現します。

@ExplicitEncrypted のアノテーションが付けられたフィールドは、常に全体として暗号化されます。次の例を考えてみましょう。

@ExplicitEncrypted(…)
String simpleValue;        (1)

@ExplicitEncrypted(…)
Address address;           (2)

@ExplicitEncrypted(…)
List<...> list;            (3)

@ExplicitEncrypted(…)
Map<..., ...> mapOfString; (4)
1null でない場合は String などの単純型の値を暗号化します。
2Address オブジェクト全体とそのすべてのネストされたフィールドを Document として暗号化します。Address#street など、Address の一部のみを暗号化するには、Address 内の street フィールドに @ExplicitEncrypted のアノテーションを付ける必要があります。
3Collection - 同様のフィールドは、エントリごとではなく単一の値として暗号化されます。
4Map -like フィールドは、キー / 値エントリとしてではなく、単一の値として暗号化されます。

クライアント側のフィールドレベル暗号化を使用すると、決定的アルゴリズムとランダム化アルゴリズムのどちらかを選択できます。選択したアルゴリズム (英語) に応じて、異なる操作 (英語) がサポートされる場合があります。特定のアルゴリズムを選択するには、@ExplicitEncrypted(algorithm) を使用します。アルゴリズム定数については、EncryptionAlgorithms を参照してください。アルゴリズムとその使用箇所の詳細については、暗号化の種類 (英語) マニュアルを参照してください。

実際の暗号化を実行するには、データ暗号化キー (DEK) が必要です。キー管理の設定方法とデータ暗号化キーの作成方法の詳細については、MongoDB ドキュメント (英語) を参照してください。DEK は、id または定義された別名を介して直接参照できます。@EncryptedField アノテーションでは、代替名による DEK の参照のみが許可されます。後述する EncryptionKeyResolver を任意の DEK に提供することが可能です。

例 1: データ暗号化キーの参照
@EncryptedField(algorithm=…, altKeyName = "secret-key") (1)
String ssn;
@EncryptedField(algorithm=…, altKeyName = "/name")      (2)
String ssn;
1 代替名 secret-key で保存された DEK を使用します。
2 実際のフィールド値を読み取り、それをキー検索に使用するフィールド参照を使用します。保存操作には常に完全なドキュメントが存在する必要があります。フィールドはクエリ / 集計では使用できません。

デフォルトでは、@ExplicitEncrypted(value= …) 属性は MongoEncryptionConverter を参照します。対応する型参照を提供することで、デフォルトの実装を変更し、任意の PropertyValueConverter 実装と交換することができます。カスタム PropertyValueConverters と必要な構成の詳細については、"プロパティコンバーター - 特定のフィールドのマッピング" セクションを参照してください。

クエリ可能な暗号化 (QE)

QE を選択すると、暗号化されたフィールドに対して、範囲等価性などのさまざまな型のクエリを実行できるようになります。
QE の機能と制限事項について詳しく知るには、読み進める前に必ず MongoDB QE ドキュメント (英語) を参照してください。

コレクションの設定

クエリ可能な暗号化では、暗号化されたフィールドに対する実際のクエリで許可される特定の側面を事前に宣言する必要があります。この情報には、使用されるアルゴリズム、許可されるクエリの種類、その属性が含まれており、コレクションの作成時に提供する必要があります。

MongoOperations#createCollection(…​) は、QE を利用したコレクションの初期設定に使用できます。Spring Data を介した QE の設定では、CSFLE と同じ構成要素(JSON スキーマの作成)を使用し、スキーマとプロパティを MongoDB に必要な設定形式に変換します。

クエリ可能な暗号化は、手動でも派生的な方法でも設定できます。

手動設定

手動設定では、暗号化フィールドの宣言方法とコレクションの作成方法を完全に制御できます。データキー、暗号化アルゴリズム、フィールドマッピングを明示的に管理する必要がある場合に便利です。

  • ✅ 暗号化設定を完全に制御

  • ✅ データキーとアルゴリズムを明示的に管理する

  • ✅ 複雑な暗号化シナリオに対応

  • ✅ 明示的な設定により予期せぬ事態のリスクを回避 (例: 不適切なアノテーションやクラスパススキャンによる構成の欠落)

  • ⚠️ 明示的なフィールド構成はドメインモデルから逸脱する可能性があるため、ドメインモデルと同期させる必要があります。

派生セットアップ

派生セットアップは、ドメインモデル内のアノテーションに基づいて、必要な暗号化フィールド設定を自動生成します。これはよりシンプルで、データモデルにすでにアノテーションが付与されている一般的な Spring アプリケーションに推奨されます。

  • ✅ ドメインモデル駆動型構成

  • ✅ セットアップとメンテナンスが簡単

  • ⚠️ 複雑なシナリオをすべて網羅しているわけではない

  • ⚠️ 予期せぬ事態が発生するリスク (例: 不適切なアノテーションやクラスパススキャンにより、サブ型に基づくドキュメントの構成が欠落している)

  • 手動収集設定

  • 派生コレクションの設定

  • MongoDB コレクション情報

BsonBinary pinDK = clientEncryption.createDataKey("local", new com.mongodb.client.model.vault.DataKeyOptions());
BsonBinary ssnDK = clientEncryption.createDataKey("local", new com.mongodb.client.model.vault.DataKeyOptions());
BsonBinary ageDK = clientEncryption.createDataKey("local", new com.mongodb.client.model.vault.DataKeyOptions());
BsonBinary signDK = clientEncryption.createDataKey("local", new com.mongodb.client.model.vault.DataKeyOptions());

CollectionOptions collectionOptions = CollectionOptions.encryptedCollection(options -> options
    .encrypted(string("pin"), pinDK)
    .queryable(encrypted(string("ssn")).algorithm("Indexed").keyId(ssnDK.asUuid()), equality().contention(0))
    .queryable(encrypted(int32("age")).algorithm("Range").keyId(ageDK.asUuid()), range().contention(8).min(0).max(150))
    .queryable(encrypted(int64("address.sign")).algorithm("Range").keyId(signDK.asUuid()), range().contention(2).min(-10L).max(10L))
);

mongoTemplate.createCollection(Patient.class, collectionOptions); (1)
1 テンプレートを使用してコレクションを作成すると、生成された keyIds をキャプチャーできない場合があります。その場合は、オプションから Document をレンダリングし、暗号化ライブラリを介して createEncryptedCollection(…​) 方式を使用してください。
class Patient {

    @Id String id;        (1)

    Address address;      (1)

    @Encrypted(algorithm = "Unindexed")
    String pin;           (2)

    @Encrypted(algorithm = "Indexed")
    @Queryable(queryType = "equality", contentionFactor = 0)
    String ssn;           (3)

    @RangeEncrypted(contentionFactor = 8, rangeOptions = "{ 'min' : 0, 'max' : 150 }")
    Integer age;          (4)

    @RangeEncrypted(contentionFactor = 0L,
        rangeOptions = "{\"min\": {\"$numberDouble\": \"0.3\"}, \"max\": {\"$numberDouble\": \"2.5\"}, \"precision\": 2 }")
    double height;        (5)
}

MongoJsonSchema patientSchema = MongoJsonSchemaCreator.create(mappingContext)
    .filter(MongoJsonSchemaCreator.encryptedOnly())
    .createSchemaFor(Patient.class);

Document encryptedFields = CollectionOptions.encryptedCollection(patientSchema)
        .getEncryptedFieldsOptions()
        .map(CollectionOptions.EncryptedFieldsOptions::toDocument)
        .orElseThrow();

template.execute(db -> clientEncryption.createEncryptedCollection(db, template.getCollectionName(Patient.class), new CreateCollectionOptions()
        .encryptedFields(encryptedFields), new CreateEncryptedCollectionParams("local"))); (1)
1id と address は暗号化されていません。これらのフィールドは通常通りクエリ可能です。
2pin は暗号化されていますが、クエリをサポートしていません。
3ssn は暗号化されており、等価クエリを許可します。
4age は暗号化されており、0 と 150 間の範囲クエリを可能にします。
5height は暗号化されており、0.3 と 2.5 間の範囲クエリを可能にします。

Queryable アノテーションは、暗号化されたフィールドに対して許可されるクエリ型を定義できます。@RangeEncrypted は、range クエリを許可するフィールド向けの @Encrypted と @Queryable を組み合わせたものです。提供されているアノテーションからカスタムアノテーションを作成することもできます。

{
    name: 'patient',
    type: 'collection',
    options: {
      encryptedFields: {
        escCollection: 'enxcol_.test.esc',
        ecocCollection: 'enxcol_.test.ecoc',
        fields: [
          {
            keyId: ...,
            path: 'ssn',
            bsonType: 'string',
            queries: [ { queryType: 'equality', contention: Long('0') } ]
          },
          {
            keyId: ...,
            path: 'age',
            bsonType: 'int',
            queries: [ { queryType: 'range', contention: Long('8'), min: 0, max: 150 } ]
          },
          {
            keyId: ...,
            path: 'pin',
            bsonType: 'string'
          },
          {
            keyId: ...,
            path: 'address.sign',
            bsonType: 'long',
            queries: [ { queryType: 'range', contention: Long('2'), min: Long('-10'), max: Long('10') } ]
          }
        ]
      }
    }
}
  • 同じコレクション内で QE と CSFLE の両方を使用することはできません。

  • equality 演算子を使用して range インデックスフィールドをクエリすることはできません。

  • range 演算子を使用して equality インデックスフィールドをクエリすることはできません。

  • bypassAutoEncrytion(true) を設定することはできません。

  • クエリ可能な暗号化と組み合わせて、@Encrypted 経由の自己管理暗号化キーを使用することはできません。

  • 競合はサーバー側でのみオプションであり、クライアントでは値を設定することを要求します (デフォルトは 8)。

  • たとえば、min や max などの追加オプションは、実際のフィールド型と一致する必要があります。bson 文字列を解析する際は、ターゲットの型を確実にするために、$numberLong などを使用してください。

  • クエリ可能な暗号化により、各ドキュメントに safeContent という追加フィールドが追加されます。明示的に除外しない限り、このフィールドは結果を取得する際にメモリにロードされます。

  • 完全な例については、spring-data-queryable-encryption [GitHub] (英語) を参照してください。

自動暗号化 (QE)

MongoDB は、MongoDB ドライバーの自動暗号化機能を使用することで、クエリ可能な暗号化を標準でサポートしています。自動暗号化には、明示的な暗号化 / 復号化手順を必要とせずに暗号化された読み取り / 書き込み操作を実行できる JSON スキーマが必要です。

MongoDB のドキュメントに従ってコレクションを作成するだけです。上記のセクションで概説した必要な設定を作成するためのテクニックを活用できます。

明示的な暗号化 (QE)

明示的な暗号化では、MongoDB ドライバーの暗号化ライブラリ (org.mongodb:mongodb-crypt) を使用して、ドメインモデル内のアノテーションによって提供されるメタ情報に基づいて暗号化および復号化タスクを実行します。

明示的クエリ可能暗号化の使用は公式にはサポートされていません。大胆なユーザーは、自己責任において @Encrypted と @Queryable を @ValueConverter(MongoEncryptionConverter.class) と組み合わせることができます。

MongoEncryptionConverter セットアップ

MongoEncryptionConverter のコンバーターのセットアップには、いくつかのコンポーネントが関係するため、いくつかの手順が必要です。Bean セットアップは以下で構成されます。

  1. ClientEncryption エンジン

  2. ClientEncryption および EncryptionKeyResolver で構成された MongoEncryptionConverter インスタンス。

  3. 登録された MongoEncryptionConverter Bean を使用する PropertyValueConverterFactory

EncryptionKeyResolver は、動的な DEK 解決を可能にするプロパティへのアクセスを提供する EncryptionContext を使用します。

例 2: MongoEncryptionConverter 構成のサンプル
class Config extends AbstractMongoClientConfiguration {

    @Autowired ApplicationContext appContext;

    @Bean
    ClientEncryption clientEncryption() {                                                            (1)
        ClientEncryptionSettings encryptionSettings = ClientEncryptionSettings.builder();
        // …

        return ClientEncryptions.create(encryptionSettings);
    }

    @Bean
    MongoEncryptionConverter encryptingConverter(ClientEncryption clientEncryption) {

        Encryption<BsonValue, BsonBinary> encryption = MongoClientEncryption.just(clientEncryption);
        EncryptionKeyResolver keyResolver = EncryptionKeyResolver.annotated((ctx) -> …);             (2)

        return new MongoEncryptionConverter(encryption, keyResolver);                                (3)
    }

    @Override
    protected void configureConverters(MongoConverterConfigurationAdapter adapter) {

        adapter
            .registerPropertyValueConverterFactory(PropertyValueConverterFactory.beanFactoryAware(appContext)); (4)
    }
}
1com.mongodb.client.vault.ClientEncryption を使用して Encryption エンジンをセットアップします。インスタンスはステートフルであるため、使用後は閉じる必要があります。ClientEncryption は Closeable であるため、Spring がこれを処理します。
2 アノテーションから EncryptionKey を決定するには、アノテーションベースの EncryptionKeyResolver をセットアップします。
3MongoEncryptionConverter を作成します。
4BeanFactory からの PropertyValueConverter ルックアップを有効にします。