暗号化サンプルアプリは、MongoDB の クライアント側フィールドレベル暗号化(CSFLE)を使用して、機密データを排除する手順を強化する方法を示しています。
サンプルアプリケーションについて
消去する権限(忘れる権限とも呼ばれます)は、 GDPRなどのルールとルールの下で個人に付与される権限です。つまり、個人の個人データを保存している会社はリクエストに応じてデータを削除できる必要があります。データは複数のシステムに分散されるため、これらの会社ではデータを識別してすべての場所から排除するのは技術的に困難である可能性があります。正しく実行された場合でも、削除されたデータが将来のバックアップから復元されるリスクがあり、法的リスクや金銭的リスクが発生する可能性があります。
警告
MongoDB、この記事で説明されているソリューションとメソッドが、削除の権利に関するすべての規制要件を満たす保証はありません。組織は、 GDPRなどの規制要件に準拠するための適切で十分な測定値を決定する必要があります。
暗号化シャーディングのサンプルアプリケーションは、消去する権限を実装する方法の 1 つを示しています。デモアプリケーションは、ユーザーの追加、ログイン、データ入力用のフロントエンドを備えたPython (Flask)Webアプリケーションです。また、暗号化シャーディング機能を紹介する「Admin」ページも含まれています。
暗号化シャーディングとは
暗号化シャーディング (暗号化消去とも呼ばれます)は、暗号化されたデータを破棄する代わりに、その復号に必要な暗号化のキーを破棄するデータ破棄手法です。これにより、データは不可分になります。
例、複数のユーザーのデータを保存しているとします。最初に、各ユーザーに独自のデータ暗号化キー(DEK)を付与し、それをそのカスタマーにマッピングします。
図では、「ユーザー A」と「ユーザー B」はそれぞれ、キー ストア内に独自の DEK を持っています。各キーは、それぞれのユーザーのデータを暗号化または復号化するために使用されます。

ユーザー B のすべてのデータを削除するとします。ユーザー B の DEK を削除すると、ユーザー B のデータを復号化できなくなります。データストア内のすべてのものが不可分化可能な暗号テキストになります。DEK は引き続き存在するため、ユーザー A のデータは影響を受けません。

CSFLE とは
CSFLE を使用すると、アプリケーションはサーバーにデータを送信する 前に、ドキュメント内の機密フィールドを暗号化できます。メモリ内でデータベースによってデータが使用される場合でも、プレーンテキストになることはありません。データベースは、クライアントによってのみ復号化されたデータを保存および送信します。
CSFLE は、最上位のエンベロープ キー(「カスタマー マスター キー」、または CMK とも呼ばれる)によって暗号化されたデータキーを使用してプレーンテキスト データを暗号化する方法であるエンベロープ暗号化を使用します。

暗号化キー管理
CMK は通常、 KMS(KMS)によって管理されます。 CSFLE は、 Amazon Web Services (Amazon Web Services)、 Azure Key Vault、 Google Cloud Platform (GCP)、KMIP 基準をサポートするキーストア( Hashiorg Keyvault など)を含む複数の KMS をサポートしています。サンプルアプリは、Amazon Web Services をKMS として使用します。
自動および明示的な暗号化
CSFLE は、自動 モードまたは 明示的モード、または両方の組み合わせで使用できます。サンプルアプリでは明示的な暗号化を使用します。
自動暗号化では、定義された暗号化スキーマに基づいて暗号化された読み取りおよび書込み操作が実行されるため、フィールドを暗号化または復号化する方法を指定するためにアプリケーションコードは必要ありません。
明示的な暗号化では、 MongoDBドライバーの暗号化ライブラリを使用して、アプリケーション内のフィールドを手動で暗号化または復号化します。
サンプルアプリのウォークスルー
サンプルアプリでは、 CSFLE と明示的な暗号化を使用し、 Amazon Web Services をKMS として使用します。

ユーザーの追加
アプリはapp.mongodb_encryption_client
オブジェクトを初期化して ClientEncryption
クラスをインスタンス化します。この暗号化クライアントは、DEK の生成と、 Amazon Web Services KMS の CMK を使用してそれらを暗号化します。
ユーザーがサインアップすると、アプリケーションはcreate_data_key
メソッドを使用してそのユーザーの一意の DEK を生成し、data_key_id
を返します。
# flaskapp/db_queries.py def create_key(userId): data_key_id = \app.mongodb_encryption_client.create_data_key (kms_provider, master_key, key_alt_names=[userId]) return data_key_id
次に、アプリはユーザー情報を保存するときにこのメソッドを使用します。
# flaskapp/user.py def save(self): dek_id = db_queries.create_key(self.username) result = app.mongodb[db_name].user.insert_one( { "username": self.username, "password_hash": self.password_hash, "dek_id": dek_id, "createdAt": datetime.now(), } ) if result: self.id = result.inserted_id return True else: return False
データの追加と暗号化
登録が完了すると、ユーザーはログし、入力フォームからキーと値のペアとしてデータを入力できます。

データベースは、このデータを「データ」という名前のMongoDBコレクションに保存し、各ドキュメントにはユーザー名とキーと値のペアが含まれています。
{ "name": "shoe size", "value": "10", "username": "tom" }
サンプルアプリはvalue
フィールドと username
フィールドを暗号化しますが、name
は暗号化しません。このアプリは、ユーザーの DEK と指定された暗号化アルゴリズムを使用してフィールドを暗号化します。
# flaskapp/db_queries.py # Fields to encrypt, and the algorithm to encrypt them with ENCRYPTED_FIELDS = { # Deterministic encryption for username, because we need to search on it "username": Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic, # Random encryption for value, as we don't need to search on it "value": Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random, }
insert_data
関数は暗号化されていないドキュメントを受け取り、ENCRYPTED_FIELDS
をループ処理してドキュメントを暗号化します。
# flaskapp/db_queries.py def insert_data(document): document["username"] = current_user.username # Loop over the field names (and associated algorithm) we want to encrypt for field, algo in ENCRYPTED_FIELDS.items(): # if the field exists in the document, encrypt it if document.get(field): document[field] = encrypt_field(document[field], algo) # Insert document (now with encrypted fields) to the data collection app.data_collection.insert_one(document)
指定されたフィールドがドキュメントに存在する場合、この関数は encrypt_field
を呼び出して、指定されたアルゴリズムを使用してドキュメントを暗号化します。
# flaskapp/db_queries.py # Encrypt a single field with the given algorithm def encrypt_field(field, algorithm): try: field = app.mongodb_encryption_client.encrypt( field, algorithm, key_alt_name=current_user.username, ) return field except pymongo.errors.EncryptionError as ex: # Catch this error in case the DEK doesn't exist. Log a warning and # re-raise the exception if "not all keys requested were satisfied" in ex._message: app.logger.warn( f"Encryption failed: could not find data encryption key for user: {current_user.username}" ) raise ex
データを追加すると、 Webアプリでそのデータが表示されます。

暗号化キーの削除
次に、DEK を削除するときに何が起こるかを確認します。このサンプルアプリは、管理ページからこれを実行します。このアプリは、キーを管理する認可を持つユーザーのみに制限する必要があります。

「データ暗号化のキーの削除」オプションは DEK を削除しますが、ユーザーの暗号化されたデータはその場に残ります。その後、アプリケーションはデータを復号化できなくなります。ログインしたユーザーのデータを取得しようとすると、エラーがスローされます。

注意
DEK60 を削除した後も、アプリケーションは、キャッシュが期限切れになるまで、データを復号化して表示できる場合があります。
しかし、データベースに実際に残っているものは何ですか。管理 ページに戻り、Fetch data for all users をクリックすると、情報を確認できます。このビューでは、アプリケーションがデータを復号化できない場合でも、例外はスローされません。代わりに、データベースに保存されている内容が正確に表示されます。
ユーザーのデータを実際に削除していなくても、データ暗号化のキーが存在しないため、アプリケーションは暗号化されたフィールドの暗号化フィールド「ユーザー名」と「value」の暗号テキストのみを表示できます。

このデータを取得するために使用されるコードは次のとおりです。前に紹介した encrypt
メソッドと同様のロジックを使用します。このアプリケーションは、フィルタなしで find
操作を実行してすべてのデータを取得し、ENCRYPTED_FIELDS
辞書をループしてフィールドを復号化します。
# flaskapp/db_queries.py def fetch_all_data_unencrypted(decrypt=False): results = list(app.data_collection.find()) if decrypt: for field in ENCRYPTED_FIELDS.keys(): for result in results: if result.get(field): result[field], result["encryption_succeeded"] = decrypt_field(result[field]) return results
decrypt_field
関数は復号化するフィールドごとに呼び出されますが、この場合、DEK がないために正常に復号化できない場合、アプリケーションはエラーをキャッチします。
# flaskapp/db_queries.py # Try to decrypt a field, returning a tuple of (value, status). This will be either (decrypted_value, True), or (raw_cipher_text, False) if we couldn't decrypt def decrypt_field(field): try: # We don't need to pass the DEK or algorithm to decrypt a field field = app.mongodb_encryption_client.decrypt(field) return field, True # Catch this error in case the DEK doesn't exist. except pymongo.errors.EncryptionError as ex: if "not all keys requested were satisfied" in ex._message: app.logger.warn( "Decryption failed: could not find data encryption key to decrypt the record." ) # If we can't decrypt due to missing DEK, return the "raw" value. return field, False raise ex
また、 mongosh シェルを使用してデータベースを直接確認し、読み取り可能なものがないことを証明することもできます。

この点では、ユーザーの暗号化されたデータはまだ存在します。データベースのバックアップからなど、暗号化のキーを復元することで、このキーにアクセスできるようになりました。
これを防ぐために、サンプルアプリケーションでは、2 つの異なるデータベースクラスターを使用します。1 つはデータの保存用で、もう 1 つは DEK(「キーヴォールト」)の保存用です。別々のクラスターを使用すると、アプリケーションデータとキーヴォールトのバックアップの復元は切り離されます。バックアップからデータクラスターを復元しても、キーヴォールト クラスターから削除された DEK は復元されません。
まとめ
クライアント側のフィールドレベル暗号化により、特定のデータを「忘れる」タスクを簡素化できます。データキーを削除すると、複数のデータベース、コレクション、バックアップ、ログにまたがるデータを実質的に忘れることができます。
実稼働アプリケーションでは、暗号化のキーを削除するだけでなく、暗号化されたデータ自体を削除することもできます。この 「深度選択」 アプローチは、データを実際に排除するのに役立ちます。データ削除に暗号化シャーディングを実装すると、削除操作が失敗した場合、または消去する必要があるデータが含まれない場合の影響を最小限に抑えられます。