MongoDB 7.0には、より高いデータベースセキュリティを必要とするシナリオ向けのQueryable Encryption機能が付属しています。 このトピックでは、Queryable Encryption機能の使用方法について説明します。
背景情報
ApsaraDB for MongoDBが提供する透過的なデータ暗号化およびディスク暗号化機能は、保存時の暗号化ソリューションです。
データ保護: ディスク上のデータを不正アクセスから保護します。 悪意のあるユーザが、データが格納されているHDDやSSDに物理的にアクセスしても、暗号化されていないデータにアクセスできない。
リーク防止: データセンターでセキュリティイベントが発生した場合やラップトップが紛失した場合など、ストレージデバイスが盗まれたり紛失したりした場合、暗号化により、権限のないユーザーが機密データにアクセスできなくなります。
コンプライアンス要件: 複数の業界標準と規制により、企業は機密データを暗号化する必要があります。 機密データには、ユーザのプライベートデータや財務情報などの情報が含まれます。 保存時の暗号化ソリューションは、企業が規制要件を満たすのに役立ちます。
TDEまたはディスク暗号化が有効になっているApsaraDB for MongoDBインスタンスのバックアップファイルは暗号化されます。
保存時の暗号化ソリューションを使用する場合、メモリに読み込まれたデータは平文のままです。 データを完全に保護するために、Secure Sockets Layer (SSL) やTransport Layer security (TLS) などのネットワーク暗号化、データベースアクセス制御、監査、監視などの追加のセキュリティ対策を実装することをお勧めします。 Alibaba Cloudの内部O&M担当者からデータベースサービスをホストするElastic Compute Service (ECS) インスタンスへのアクセスに関する懸念を排除するために、Alibaba Cloudはセキュリティリスクを防ぐために顧客認証と強制監査を提供しています。
データベースのセキュリティに対する要件が高く、追加の暗号化方法が必要な場合は、MongoDB 7.0で正式にリリースされたQueryable encryption機能を使用できます。
概要
Queryable Encryption機能のプレビューバージョンはMongoDB 6.0でリリースされ、正式バージョンはMongoDB 7.0でリリースされます。
Queryable Encryption機能を使用すると、データがクライアントに到達するまでデータを暗号化し続けることができます。 クエリは、key Management Service (KMS) によって管理される暗号化キーとともにサーバーに対して行われます。 それから。 データが照会され、サーバー上の暗号文で返されます。 データがクライアントに返された後、データはキーを使用して復号化され、平文で表示されます。
Queryable Encryption機能には、次の機能があります。
クライアントからの機密データを暗号化し、クライアントのみが暗号化キーを取得できるようにします。
データの送信、保存、使用、監査、バックアップなど、データライフサイクル全体でデータを暗号化します。
クライアントは、暗号化されたデータに対して、同等性、範囲、プレフィックス、サフィックス、部分文字列クエリなどの表現クエリを実行できます。
データプライバシー保護のパフォーマンスを向上させます。 サーバー上のアプリケーションにアクセスし、暗号化キーを使用できる許可されたユーザーのみが、平文でデータを表示できます。
機密データを含むアプリケーションの開発を容易にします。 開発者は、データベースに付属の包括的な暗号化機能を直接使用して、セキュリティとコンプライアンスを確保できます。
ApsaraDB for MongoDBに機密データを保存するAlibaba Cloudユーザーのセキュリティ上の懸念を軽減します。
MongoDB Community Editionによってリリースされた機能は、Enterprise Edition (Atlas) によってリリースされた機能とはわずかに異なります。 MongoDB Community Editionは自動暗号化をサポートしていません。
ドライバーのバージョンと暗号化されたデータベースのバージョンの詳細については、「Queryable Encryptionの互換性」をご参照ください。
制限事項
暗号化されたコレクションの診断コマンドとクエリログの結果はさらに編集または非表示になり、問題分析が損なわれます。
aggregate、count、find、insert、update、delete
など、暗号化されたコレクションに適用されるコマンドは、スロークエリログとプロファイラーには記録されません。collStats、currentOp、top、$planCacheStats
などの診断コマンドの結果はさらに編集され、結果の一部のフィールドは非表示になります。
暗号化フィールド間の競合と競合は、書き込み遅延を増加させる可能性があります。 フィールドは、デフォルトの競合が8のときに完了します。
サイズが1 GBを超えるメタデータコレクションは、手動で圧縮する必要があります。 詳細については、「メタデータコレクションの圧縮」をご参照ください。
encryptedFieldsMap
オブジェクトは、オブジェクト内のクエリフィールドも含めて変更できません。Queryable Encryption機能は、レプリカセットまたはシャードクラスターインスタンスでのみサポートされます。
セカンダリノードでQueryable Encryptionが有効になっているデータを読み取ることはできません。
updateManyまたはbulkWrite
コマンドを実行してドキュメントを一括で更新することはできません。また、findAndModify
コマンドのパラメーターは制限されています。upsertセマンティクスはサポートされていません。 upsertがトリガーされると、暗号化されたフィールドは挿入されません。
クライアント側フィールドレベル暗号化 (CSFLE) 機能は、クエリ可能暗号化機能とともにコレクションに対して有効にすることはできません。また、CSFLEが有効になっているコレクションまたは暗号化されていないコレクションは、クエリ可能暗号化が有効になっているコレクションに変換することはできません。
Queryable Encryption機能は、新しい空のコレクションに対してのみ有効にできます。
暗号化されたフィールドを含むコレクションの名前は変更できません。
$rename
コマンドを実行してフィールドの名前を変更することはできません。暗号化コレクションの作成時に
jsonSchema
を指定した場合、encrypt
キーワードを含めることはできません。ビュー、時系列コレクション、および上限付きコレクションはサポートされていません。
TTLインデックスまたは一意のインデックスはサポートされていません。
jsonSchema
はクローズできません。コレクションは、Queryable Encryptionが有効なMongoClientを使用して削除する必要があります。 それ以外の場合、メタデータは残ります。
Queryable Encryption機能は照合順序をサポートしていません。 照合は、暗号化されたフィールドの通常のソートをブロックします。
_id
フィールドは暗号化フィールドとして指定できません。Queryable Encryption機能でサポートされるコマンドと演算子の数は限られています。 詳細については、「クエリ可能暗号化でサポートされる操作」をご参照ください。
準備
次の例では、ECSインスタンスを検証クライアントとして使用します。 テスト環境に関連する依存関係が含まれている場合は、対応する手順をスキップできます。 mongoshは自動暗号化のみをサポートし、MongoDB Community Editionは明示的な暗号化のみをサポートします。 このセクションでは、検証にNode.jsドライバーを使用します。
Node.jsとnpmをインストールします。
curl -fsSL https://rpm.nodesource.com/setup_lts.x | sudo bash - sudo yum install nodejs node -v npm -v
MongoDB用の公式Node.jsドライバーをインストールします。
mkdir node_quickstart cd node_quickstart npm init -y npm install mongodb@6.6
libmongocryptライブラリをインストールします。
vi /etc/yum.repos.d/libmongocrypt.repo // Enter the following content in the file. [libmongocrypt] name=libmongocrypt repository baseurl=https://libmongocrypt.s3.amazonaws.com/yum/redhat/8/libmongocrypt/1.8/x86_64 gpgcheck=1 enabled=1 gpgkey=https://pgp.mongodb.com/libmongocrypt.asc // install sudo yum install -y libmongocrypt
Node.jsドライバーが依存するmongodb-client-encryptionパッケージをインストールします。
sudo yum groupinstall 'Development Tools' npm install mongodb-client-encryption
mongoshをインストールし、MONGODB_URI環境変数を設定します。
wget https://repo.mongodb.org/yum/redhat/8/mongodb-org/7.0/x86_64/RPMS/mongodb-mongosh-2.2.5.x86_64.rpm yum install -y ./mongodb-mongosh-2.2.5.x86_64.rpm export MONGODB_URI="mongodb://root:xxxxxx@dds-2zef23cef14b4f142.mongodb.pre.rds.aliyuncs.com:3717,dds-2zef23cef14b4f141.mongodb.pre.rds.aliyuncs.com:3717/admin?replicaSet=mgset-855706" // Test the connectivity. mongosh ${MONGODB_URI}
自動暗号化された共有ライブラリを取得します。
ダウンロードセンターでお使いのマシンと配布バージョンに対応するクライアントを選択し、crypt_sharedパッケージを選択します。 詳細については、「MongoDB Enterprise Serverのダウンロード」をご参照ください。
// Decompress the local directory to obtain the lib/mongo_crypt_v1.so file. tar -xzvf mongo_crypt_shared_v1-linux-x86_64-enterprise-rhel80-7.0.9.tgz
手順
MongoDB Community Editionは自動暗号化をサポートしていません。 したがって、このセクションでは、明示的な暗号化プロセスについて説明します。
Node.jsのRead-Eval-Print Loop (REPL) 環境に入り、次の手順を実行します。
node -i -e "const MongoClient = require('mongodb').MongoClient; const ClientEncryption = require('mongodb').ClientEncryption;"
顧客マスターキー (CMK) を作成します。
説明次の例は、ローカルKMSプロバイダーのサンプル設定を示しています。 運用環境では設定を使用しないことを推奨します。
96バイトのCMKを作成し、ローカルファイルシステムの
customer-master-key.txt
ファイルにCMKを保存します。const fs = require("fs"); const crypto = require("crypto"); try { fs.writeFileSync("customer-master-key.txt" 、crypto.randomBytes(96)); } catch (err) { console.error(err); }
この例では、Node.jsを使用してランダムな文字列を呼び出し、96バイトのCMKを生成します。 mongoシェルで
/dev/urandom
を使用して、96バイトのCMKを生成することもできます。echo $(head -c 96 /dev/urandom | base64 | tr -d '\n')
変数を初期化します。
// KMS provider name should be one of the following: "aws", "gcp", "azure", "kmip" or "local" const kmsProviderName = "local"; const uri = process.env.MONGODB_URI; const keyVaultDatabaseName = "encryption"; const keyVaultCollectionName = "__keyVault"; const keyVaultNamespace = "encryption.__keyVault"; const encryptedDatabaseName = "medicalRecords"; const encryptedCollectionName = "patients";
上記のサンプルコードでは、次の変数が初期化されます。
kmsProviderName
: KMSプロバイダーの名前。 この例では、local
が使用されます。uri
: MongoDB URI。 MongoDB URIは、MONGODB_URI
環境変数で指定できます。 MongoDB URIを指定することもできます。keyVaultDatabaseName
: データ暗号化キー (DEK) を格納するデータベースの名前。keyVaultCollectionName
: DEKを格納するコレクションの名前。 コレクションは通常のコレクションとは異なる必要があります。keyVaultNamespace
:keyVaultDatabaseName
またはkeyVaultCollectionName
変数に等しい。encryptedDatabaseName
: 暗号化されたデータを格納するデータベースの名前。encryptedCollectionName
: 暗号化されたデータを格納するコレクションの名前。
DEKを格納するコレクションに一意のインデックスを作成します。
const keyVaultClient = new MongoClient(uri); await keyVaultClient.connect(); const keyVaultDB = keyVaultClient.db(keyVaultDatabaseName); // Delete the database with the same name as the database that stores DEKs to prevent excess data. await keyVaultDB.dropDatabase(); const keyVaultColl = keyVaultDB.collection(keyVaultCollectionName); await keyVaultColl.createIndex( { keyAltNames: 1 }, { unique: true, partialFilterExpression: { keyAltNames: { $exists: true } }, } ); // double check await keyVaultColl.indexes();
暗号化コレクションを作成します。
作成したCMKを取得し、KMSプロバイダーを指定します。
const localMasterKey = fs.readFileSync("./customer-master-key.txt"); kmsProviders = {local: {key: localMasterKey }};
DEKを作成します。
説明この手順を実行する前に、
uri
変数で指定されたユーザーがencryption._keyVault
およびmedicalRecords
データベースに対するdbAdmin権限を持っていることを確認してください。const clientEnc = new ClientEncryption(keyVaultClient, { keyVaultNamespace: keyVaultNamespace、 kmsProviders: kmsProviders, }); const dek1 = await clientEnc.createDataKey(kmsProviderName, {) keyAltNames: ["dataKey1"] 、}); const dek2 = await clientEnc.createDataKey(kmsProviderName, {) keyAltNames: ["dataKey2"] 、});
暗号化するフィールドを指定し、作成したDEKを設定します。
const encryptedFieldsMap = { [`${encryptedDatabaseName}.${encryptedCollectionName}`]: { fields: [ { keyId: dek1, path: "patientId", bsonType: "int", queries: { queryType: "equality" }, }, { keyId: dek2, path: "medications", bsonType: "array", }, ], }, };
自動的に暗号化された共有ライブラリを指定し、MongoClientを作成します。
const extraOptions = {cryptSharedLibPath: "/root/lib/mongo_crypt_v1.so"}; const encClient = new MongoClient(uri, { autoEncryption: { keyVaultNamespace, kmsProviders, extraOptions, encryptedFieldsMap, }, }); await encClient.connect();
暗号化コレクションを作成します。
const newEncDB = encClient.db(encryptedDatabaseName); wait newEncDB.dropDatabase(); await newEncDB.createCollection(encryptedCollectionName);
読み書き操作の暗号化に使用されるMongoClientを作成します。
作成したDEKを格納するコレクションを指定します。
const eDB = "encryption"; const eKV = "__keyVault"; const keyVaultNamespace = `${eDB}.${eKV}`; const secretDB = "medicalRecords"; const secretCollection = "patients";
作成したCMKを指定します。
重要本番環境ではローカルキーファイルを使用しないでください。
const fs = require("fs"); const path = "./customer-master-key.txt"; const localMasterKey = fs.readFileSync(path); const kmsProviders = { local: { key: localMasterKey, }, };
作成されたDEKを取得します。
説明DEK名は、ステップ4の2次サブステップで作成されたDEKの名前と同じでなければなりません。
const uri = process.env.MONGODB_URI;; const unencryptedClient = new MongoClient(uri); unencryptedClient.connect(); const keyVaultClient = unencryptedClient.db(eDB).collection(eKV); const dek1 = await keyVaultClient.findOne({ keyAltNames: "dataKey1" }); const dek2 = await keyVaultClient.findOne({ keyAltNames: "dataKey2" });
自動的に暗号化された共有ライブラリを指定し、MongoClientを作成します。
const extraOptions = { cryptSharedLibPath: "/root/lib/mongo_crypt_v1.so" 、}; const encryptedClient = new MongoClient(uri, { autoEncryption: { kmsProviders: kmsProviders, keyVaultNamespace: keyVaultNamespace、 bypassQueryAnalysis: true、 keyVaultClient: unencryptedClient、 extraOptions: extraOptions、 }, }); wait encryptedClient.connect();
ClientEncryptionオブジェクトを作成します。
const encryption = new ClientEncryption(unencryptedClient, {) keyVaultNamespace, kmsProviders, });
作成した暗号化コレクションに、暗号化フィールドを含むドキュメントを挿入します。
const patientId = 12345678; const medications = ["Atorvastatin", "Levothyroxine"]; const indexedInsertPayload = await encryption.encrypt(patientId, { algorithm: "Indexed", keyId: dek1._id, contentionFactor: 1, }); const unindexedInsertPayload = await encryption.encrypt(medications, { algorithm: "Unindexed", keyId: dek2._id, }); const encryptedColl = encryptedClient.db(secretDB).collection(secretCollection); await encryptedColl.insertOne({ firstName: "Jon", patientId: indexedInsertPayload, medications: unindexedInsertPayload, });
作成した暗号化コレクションに対してフィールドレベルのクエリを実行します。
const findPayload = await encryption.encrypt(patientId, {) algorithm: "Indexed" 、 keyId: dek1._id、 queryType: "equality" 、 contentionFactor: 1, }); console.log (encryptedColl.findOne({ patientId: findPayload }));
次の図は、クエリ結果のサンプルを示しています。
暗号化オプションを含むクライアントを使用して、暗号化フィールドにアクセスします。 そうでない場合、暗号化フィールドにアクセスできません。
フィールドレベルのクエリに対して暗号化されていない
unencryptedClient
クライアントを使用します。console.log (unencryptedClient.db(secretDB).collection(secretCollection).findOne());
次の図は、クエリ結果のサンプルを示しています。
mongoshを使用して、暗号化されたフィールドに外部からアクセスすることもできます。 これは、クライアントキーなしで作成された暗号化コレクションへのアクセスをシミュレートします。
// Open another terminal session and use mongosh to connect to the MongoDB URI. mongosh ${MONGODB_URI} db.getSiblingDB("medicalRecords").patients.findOne()
サンプル結果を次の図に示します。