MongoDB 7.0 menyertakan fitur Queryable Encryption untuk skenario yang memerlukan keamanan database lebih tinggi. Topik ini menjelaskan cara menggunakan fitur tersebut.
Informasi latar belakang
Fitur transparent data encryption dan disk encryption yang disediakan oleh ApsaraDB for MongoDB merupakan solusi enkripsi saat diam (encryption at rest). Solusi ini bertujuan untuk:
Proteksi data: Melindungi data pada disk dari akses tidak sah. Bahkan jika pengguna jahat memiliki akses fisik ke HDD atau SSD tempat data disimpan, data tersebut tetap tidak dapat diakses dalam bentuk teks biasa.
Pencegahan kebocoran: Jika perangkat penyimpanan dicuri atau hilang—misalnya akibat insiden keamanan di pusat data atau kehilangan laptop—enkripsi memastikan bahwa data sensitif tidak dapat diakses oleh pihak yang tidak berwenang.
Kepatuhan terhadap regulasi: Berbagai standar industri dan peraturan mewajibkan perusahaan untuk mengenkripsi data sensitif, seperti informasi pribadi pengguna dan data keuangan. Solusi enkripsi saat diam membantu perusahaan memenuhi persyaratan regulasi tersebut.
File backup dari instans ApsaraDB for MongoDB yang telah mengaktifkan TDE atau enkripsi disk juga dienkripsi.
Dengan solusi enkripsi saat diam, data yang dibaca ke memori tetap dalam bentuk teks biasa. Untuk perlindungan menyeluruh, kami menyarankan penerapan langkah-langkah keamanan tambahan seperti enkripsi jaringan—termasuk Secure Sockets Layer (SSL) atau Transport Layer Security (TLS)—kontrol akses database, auditing, dan pemantauan. Guna mengatasi kekhawatiran mengenai akses personel O&M internal Alibaba Cloud ke instance Elastic Compute Service (ECS) yang menjalankan layanan database Anda, Alibaba Cloud menyediakan mekanisme otorisasi pelanggan dan auditing wajib untuk mencegah risiko keamanan.
Jika Anda memiliki persyaratan keamanan database yang lebih ketat dan memerlukan metode enkripsi tambahan, Anda dapat menggunakan fitur Queryable Encryption yang secara resmi dirilis di MongoDB 7.0.
Pendahuluan
Versi pratinjau fitur Queryable Encryption dirilis di MongoDB 6.0, sedangkan versi resminya tersedia mulai MongoDB 7.0.
Fitur Queryable Encryption memungkinkan data tetap terenkripsi hingga mencapai client. Kueri dikirim ke server bersama kunci enkripsi yang dikelola oleh Key Management Service (KMS). Data kemudian dikueri dan dikembalikan dalam bentuk ciphertext di sisi server, lalu didekripsi menggunakan kunci tersebut setelah tiba di client sehingga ditampilkan dalam bentuk teks biasa.
Fitur Queryable Encryption menyediakan kemampuan berikut:
Mengenkripsi data sensitif dari sisi client dan hanya memungkinkan client yang memiliki kunci enkripsi untuk mengaksesnya.
Mengenkripsi data sepanjang seluruh siklus hidupnya, termasuk saat transmisi, penyimpanan, penggunaan, auditing, dan pencadangan.
Memungkinkan client menjalankan kueri ekspresif pada data terenkripsi, termasuk kueri equality, range, prefix, suffix, dan substring.
Meningkatkan kinerja perlindungan privasi data. Hanya pengguna yang berwenang—yang dapat mengakses aplikasi di server dan menggunakan kunci enkripsi—yang dapat melihat data dalam bentuk teks biasa.
Mempermudah pengembangan aplikasi yang melibatkan data sensitif. Developer dapat langsung memanfaatkan kemampuan enkripsi komprehensif yang disediakan database untuk memastikan keamanan dan kepatuhan.
Mengurangi kekhawatiran keamanan bagi pengguna Alibaba Cloud yang ingin menyimpan data sensitif di ApsaraDB for MongoDB.
Fitur yang dirilis oleh MongoDB Community Edition sedikit berbeda dari yang tersedia di Enterprise Edition (Atlas). MongoDB Community Edition tidak mendukung enkripsi otomatis.
Untuk informasi lebih lanjut mengenai versi driver dan versi database terenkripsi, lihat Kompatibilitas Queryable Encryption.
Batasan
Hasil perintah diagnostik dan log kueri pada koleksi terenkripsi akan diedit atau disembunyikan, sehingga mengganggu analisis masalah:
Perintah yang berlaku untuk koleksi terenkripsi, seperti
aggregate, count, find, insert, update, dan delete, tidak dicatat dalam slow query log dan profiler.Hasil perintah diagnostik, seperti
collStats, currentOp, top, atau $planCacheStats, diedit dan beberapa field dalam hasilnya disembunyikan.
Persaingan dan konflik antar field terenkripsi dapat meningkatkan latensi penulisan. Field tersebut selesai diproses ketika contention default bernilai 8.
Koleksi metadata yang ukurannya melebihi 1 GB harus dikompaksi secara manual. Untuk informasi lebih lanjut, lihat Kompaksi Koleksi Metadata.
Objek
encryptedFieldsMaptidak dapat diubah, termasuk field kueri di dalam objek tersebut.Fitur Queryable Encryption hanya didukung oleh instans replica set atau sharded cluster.
Data dengan Queryable Encryption yang diaktifkan pada node secondary tidak dapat dibaca.
Dokumen tidak dapat diperbarui secara batch dengan menjalankan perintah
updateMany atau bulkWrite, dan parameter dalam perintahfindAndModifydibatasi.Semantik upsert tidak didukung. Saat upsert dipicu, field terenkripsi tidak dimasukkan.
Fitur Client-Side Field Level Encryption (CSFLE) tidak dapat diaktifkan bersamaan dengan fitur Queryable Encryption pada satu koleksi, dan koleksi yang telah mengaktifkan CSFLE atau koleksi tanpa enkripsi tidak dapat dikonversi menjadi koleksi dengan Queryable Encryption yang diaktifkan.
Fitur Queryable Encryption hanya dapat diaktifkan untuk koleksi kosong yang baru dibuat.
Koleksi yang berisi field terenkripsi tidak dapat diganti namanya. Field tersebut tidak dapat diganti namanya dengan menjalankan perintah
$rename.Jika
jsonSchemaditentukan saat membuat koleksi terenkripsi, kata kunciencrypttidak boleh disertakan.Views, koleksi time series, dan capped collections tidak didukung.
Indeks TTL atau indeks unik tidak didukung.
jsonSchematidak dapat ditutup.Koleksi harus dihapus menggunakan MongoClient dengan Queryable Encryption yang diaktifkan. Jika tidak, metadata akan tetap tersisa.
Fitur Queryable Encryption tidak mendukung collation. Collation menghalangi pengurutan normal untuk field terenkripsi.
Field
_idtidak dapat ditentukan sebagai field terenkripsi.Hanya jumlah terbatas perintah dan operator yang didukung oleh fitur Queryable Encryption. Untuk informasi lebih lanjut, lihat Operasi yang Didukung untuk Queryable Encryption.
Persiapan
Panduan ini menggunakan instance ECS sebagai client untuk mendemonstrasikan prosedurnya. Jika lingkungan pengujian Anda sudah memiliki dependensi yang diperlukan, Anda dapat melewati langkah-langkah terkait. Panduan ini menggunakan driver Node.js untuk demonstrasi karena mongosh hanya mendukung enkripsi otomatis, sedangkan MongoDB Community Edition hanya mendukung enkripsi eksplisit.
-
Instal Node.js dan npm.
curl -fsSL https://rpm.nodesource.com/setup_lts.x | sudo bash - sudo yum install nodejs node -v npm -v Instal driver Node.js resmi untuk MongoDB.
mkdir node_quickstart cd node_quickstart npm init -y npm install mongodb@6.6-
Instal library libmongocrypt.
vi /etc/yum.repos.d/libmongocrypt.repo // Tambahkan konten berikut ke 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 // Instal library sudo yum install -y libmongocrypt Instal paket mongodb-client-encryption yang menjadi dependensi driver Node.js.
sudo yum groupinstall 'Development Tools' npm install mongodb-client-encryption-
Instal mongosh dan konfigurasikan variabel lingkungan 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" // Uji koneksi mongosh ${MONGODB_URI} Dapatkan shared library enkripsi otomatis.
Pilih client yang sesuai dengan mesin dan versi distribusi Anda di Pusat Unduhan, lalu pilih paket crypt_shared. Untuk informasi lebih lanjut, lihat Unduhan MongoDB Enterprise Server.
// Ekstrak direktori lokal untuk mendapatkan file lib/mongo_crypt_v1.so. tar -xzvf mongo_crypt_shared_v1-linux-x86_64-enterprise-rhel80-7.0.9.tgz
Prosedur
MongoDB Community Edition tidak mendukung enkripsi otomatis. Oleh karena itu, artikel ini mendemonstrasikan proses enkripsi eksplisit.
Masuk ke lingkungan Read-Eval-Print Loop (REPL) Node.js, lalu lakukan langkah-langkah berikut:
node -i -e "const MongoClient = require('mongodb').MongoClient; const ClientEncryption = require('mongodb').ClientEncryption;"
-
Buat customer master key (CMK).
CatatanContoh berikut menunjukkan konfigurasi contoh penyedia KMS lokal. Kami menyarankan agar Anda tidak menggunakan konfigurasi ini di lingkungan produksi.
Buat CMK sepanjang 96 byte dan simpan dalam file
customer-master-key.txtpada sistem file lokal.const fs = require("fs"); const crypto = require("crypto"); try { fs.writeFileSync("customer-master-key.txt", crypto.randomBytes(96)); } catch (err) { console.error(err); }Contoh ini menghasilkan CMK menggunakan panggilan crypto Node.js. Sebagai alternatif, Anda dapat menghasilkan CMK 96-byte di shell menggunakan
/dev/urandom:echo $(head -c 96 /dev/urandom | base64 | tr -d '\n') Inisialisasi variabel.
// Nama penyedia KMS harus salah satu dari berikut: "aws", "gcp", "azure", "kmip" atau "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";Pada kode contoh di atas, variabel berikut diinisialisasi:
kmsProviderName: nama penyedia KMS. Pada contoh ini, digunakanlocal.uri: URI MongoDB. URI MongoDB dapat ditentukan melalui variabel lingkunganMONGODB_URIatau secara langsung.keyVaultDatabaseName: nama database yang menyimpan data encryption keys (DEKs).keyVaultCollectionName: nama koleksi yang menyimpan DEKs. Koleksi ini harus berbeda dari koleksi biasa.keyVaultNamespace: sama dengan variabelkeyVaultDatabaseNameataukeyVaultCollectionName.encryptedDatabaseName: nama database yang menyimpan data terenkripsi.encryptedCollectionName: nama koleksi yang menyimpan data terenkripsi.
Buat indeks unik pada koleksi yang menyimpan DEKs.
const keyVaultClient = new MongoClient(uri); await keyVaultClient.connect(); const keyVaultDB = keyVaultClient.db(keyVaultDatabaseName); // Hapus database dengan nama yang sama dengan database penyimpan DEKs untuk mencegah data berlebih. await keyVaultDB.dropDatabase(); const keyVaultColl = keyVaultDB.collection(keyVaultCollectionName); await keyVaultColl.createIndex( { keyAltNames: 1 }, { unique: true, partialFilterExpression: { keyAltNames: { $exists: true } }, } ); // periksa ulang await keyVaultColl.indexes();Buat koleksi terenkripsi.
Ambil CMK yang telah dibuat dan tentukan penyedia KMS.
const localMasterKey = fs.readFileSync("./customer-master-key.txt"); kmsProviders = {local: {key: localMasterKey}};Buat DEK.
CatatanSebelum melakukan langkah ini, pastikan pengguna yang ditentukan dalam variabel
urimemiliki izin dbAdmin pada databaseencryption._keyVaultdanmedicalRecords.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"], });Tentukan field yang akan dienkripsi dan konfigurasikan DEK yang telah dibuat.
const encryptedFieldsMap = { [`${encryptedDatabaseName}.${encryptedCollectionName}`]: { fields: [ { keyId: dek1, path: "patientId", bsonType: "int", queries: { queryType: "equality" }, }, { keyId: dek2, path: "medications", bsonType: "array", }, ], }, };Tentukan shared library enkripsi otomatis dan buat MongoClient.
const extraOptions = {cryptSharedLibPath: "/root/lib/mongo_crypt_v1.so"}; const encClient = new MongoClient(uri, { autoEncryption: { keyVaultNamespace, kmsProviders, extraOptions, encryptedFieldsMap, }, }); await encClient.connect();Buat koleksi terenkripsi.
const newEncDB = encClient.db(encryptedDatabaseName); await newEncDB.dropDatabase(); await newEncDB.createCollection(encryptedCollectionName);
Buat MongoClient yang digunakan untuk mengenkripsi operasi baca dan tulis.
Tentukan koleksi yang menyimpan DEK yang telah dibuat.
const eDB = "encryption"; const eKV = "__keyVault"; const keyVaultNamespace = `${eDB}.${eKV}`; const secretDB = "medicalRecords"; const secretCollection = "patients";Tentukan CMK yang telah dibuat.
PentingJangan gunakan file kunci lokal di lingkungan produksi.
const fs = require("fs"); const path = "./customer-master-key.txt"; const localMasterKey = fs.readFileSync(path); const kmsProviders = { local: { key: localMasterKey, }, };Ambil DEK yang telah dibuat.
CatatanNama DEK harus sama dengan nama DEK yang dibuat pada sublangkah kedua Langkah 4.
const uri = process.env.MONGODB_URI;; const unencryptedClient = new MongoClient(uri); await unencryptedClient.connect(); const keyVaultClient = unencryptedClient.db(eDB).collection(eKV); const dek1 = await keyVaultClient.findOne({ keyAltNames: "dataKey1" }); const dek2 = await keyVaultClient.findOne({ keyAltNames: "dataKey2" });Tentukan shared library enkripsi otomatis dan buat 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, }, }); await encryptedClient.connect();Buat objek ClientEncryption.
const encryption = new ClientEncryption(unencryptedClient, { keyVaultNamespace, kmsProviders, });
Masukkan dokumen yang berisi field terenkripsi ke dalam koleksi terenkripsi yang telah dibuat.
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, });-
Lakukan kueri tingkat field pada koleksi terenkripsi yang telah dibuat.
const findPayload = await encryption.encrypt(patientId, { algorithm: "Indexed", keyId: dek1._id, queryType: "equality", contentionFactor: 1, }); console.log(await encryptedColl.findOne({ patientId: findPayload }));Contoh berikut menunjukkan dokumen yang dikembalikan.
> console.log(await encryptedColl.findOne({ patientId: findPayload })); { _id: new ObjectId('6645b56f58abf955ebd95caf'), firstName: 'Jon', patientId: 12345678, medications: [ 'Atorvastatin', 'Levothyroxine' ], __safeContent__: [ Binary.createFromBase64('IrPf972hlhDvnasQH6rIAW6BqERo0ZEgC6C0/zNQiIY=', 0) ] } -
Gunakan client yang berisi opsi terenkripsi untuk mengakses field terenkripsi. Jika tidak, field tersebut tidak dapat diakses.
Gunakan client
unencryptedClientyang tidak terenkripsi untuk kueri tingkat field.console.log(await unencryptedClient.db(secretDB).collection(secretCollection).findOne());Contoh berikut menunjukkan dokumen yang dikembalikan. Field sensitif muncul sebagai data biner.
> console.log(await unencryptedClient.db(secretDB).collection(secretCollection).findOne()); { _id: new ObjectId('6645b56f58abf955ebd95caf'), firstName: 'Jon', patientId: Binary.createFromBase64('DtmNrEDyTEBDidZxWkbGU/MQdYNxwmnqYj5tSr9uhHbwWj8bsSD3TWlZ8aMMvw6FY00cmdc1QLLoEX3NwlKRhz0zax9LcQhN3vKUf4eq3hAfBYWkyOQxsiwbPsU0AiXnMV+qM6J2p2JZGLrDvxfbTY+obBmRqdvlgJ51dKmYopvDNToWBXDQkqAis9/3vaGWE0+dqxAfqsgBboGoRGjRkSALoI7/M1CIhvmpds5LR7/232uI4f5QDbk0JVfjnI0Doov6b0GrAXe9', 6), medications: Binary.createFromBase64('EKqAnwBkWUBon0Qf9sZHVIkEphUdfDK/aqYPs5M1Xc58CkojwX0kvC+KjwYyEozia41F5cnD9NFBwnVuDJUaqjTLc1YwG1DEIUZdcYCMf3JiureqA0voYP3gZxPyFmf/h1DS80Jz+g', 6), __safeContent__: [ Binary.createFromBase64('IrPf972hlhDvnasQH6rIAW6BqERo0ZEgC6CO/zNQiIY=', 0) ] }Anda juga dapat menggunakan mongosh untuk mengakses field terenkripsi dari luar. Ini mensimulasikan akses ke koleksi terenkripsi yang telah dibuat tanpa kunci client.
// Di sesi terminal terpisah, sambungkan langsung ke URI MongoDB dengan mongosh. mongosh ${MONGODB_URI} db.getSiblingDB("medicalRecords").patients.findOne()Contoh berikut menunjukkan dokumen yang dikembalikan.
mgset-855706 [primary] admin> db.getSiblingDB("medicalRecords").patients.findOne() { _id: ObjectId('6645b56f58abf955ebd95caf'), firstName: 'Jon', patientId: Binary.createFromBase64('DtmNrEDyTEBYDidZxWkbGU/MQdYNxwmnqYj5tSr9uhHbwWj8bsSD3TWlZ8aMMvw6fYO0cmdc1QLoEX3Nw1KRhz0zax9LcQhN3vKUf4eq3hAfBYWkyQxsiwbPsU0AiXnMV+qM6JZGLrDvxfbTY+obBmRqdvlJ51dKmYopvDNTowBXdQkqAis9/3vaGWEO+dqxAfqsgBboGoRGjRkSALoI7/M1CIhvmpds5LR7/232uI4f5QDbk0JVfjnIODoov6b0GrAXe9', 6), medications: Binary.createFromBase64('EKqAnwBkWUBon0Qf9sZHVIkEphUdfdk/aqYPs5M1Xc58CkojX0kvC+KjwYyEozia41F5cnD9NFBwnVuDJUaqjTLc1YwG1DEIUZdcYMf3liuregA0qvoYP3qZxPyFmf/h1IJz+g', 6), __safeContent__: [ Binary.createFromBase64('IrPf972hlhDvnasQH6rIAW6BqERo0ZEgC6CO/zNQiIY=', 0) ] }