MongoDB 7.0 introduit la fonctionnalité Queryable Encryption pour les scénarios nécessitant un niveau de sécurité de base de données plus élevé.
Informations générales
Les fonctionnalités de chiffrement transparent des données (TDE) et de chiffrement de disque fournies par ApsaraDB for MongoDB sont des solutions de chiffrement au repos. Elles répondent aux objectifs suivants :
Protection des données : protège les données sur les disques contre tout accès non autorisé. Même si un utilisateur malveillant dispose d'un accès physique aux disques durs ou SSD sur lesquels les données sont stockées, il ne peut pas accéder aux données en clair.
Prévention des fuites : en cas de vol ou de perte d'un périphérique de stockage, le chiffrement garantit que les données sensibles restent inaccessibles aux utilisateurs non autorisés.
Exigences de conformité : de nombreuses normes et réglementations sectorielles imposent aux entreprises de chiffrer les données sensibles, telles que les données personnelles des utilisateurs et les informations financières. Le chiffrement au repos aide les entreprises à respecter ces exigences réglementaires.
Les fichiers de sauvegarde d'une instance ApsaraDB for MongoDB avec le TDE ou le chiffrement de disque activé sont chiffrés.
Avec le chiffrement au repos, les données chargées en mémoire restent en texte clair. Pour protéger pleinement vos données, nous vous recommandons de mettre en œuvre des mesures de sécurité supplémentaires, telles que le chiffrement réseau (SSL/TLS), le contrôle d'accès à la base de données, l'audit et la surveillance. Afin de prévenir les risques de sécurité liés à l'accès interne des opérations et de la maintenance (O&M) aux instances Elastic Compute Service (ECS) qui hébergent vos services de base de données, Alibaba Cloud propose une autorisation client et un audit obligatoire.
Si vous avez besoin d'un chiffrement supplémentaire au-delà du chiffrement au repos, vous pouvez utiliser la fonctionnalité Queryable Encryption officiellement publiée dans MongoDB 7.0.
Présentation
Queryable Encryption a été proposée en aperçu dans MongoDB 6.0 et officiellement publiée dans MongoDB 7.0.
Queryable Encryption maintient les données chiffrées jusqu'à ce qu'elles atteignent le client. Les requêtes sont envoyées au serveur avec la clé de chiffrement gérée par Key Management Service (KMS). Le serveur interroge et renvoie les données sous forme de texte chiffré. Une fois les données renvoyées au client, elles sont déchiffrées à l'aide de la clé et affichées en texte clair.
Queryable Encryption offre les capacités suivantes :
Chiffre les données sensibles depuis le côté client. Seul le client peut obtenir la clé de chiffrement.
Chiffre les données tout au long de leur cycle de vie, y compris pendant la transmission, le stockage, l'utilisation, l'audit et la sauvegarde.
Prend en charge des requêtes expressives sur les données chiffrées, notamment les requêtes d'égalité, de plage, de préfixe, de suffixe et de sous-chaîne.
Renforce la confidentialité des données. Seuls les utilisateurs autorisés ayant accès à l'application serveur et aux clés de chiffrement peuvent afficher les données en texte clair.
Simplifie le développement des applications. Les développeurs peuvent utiliser les capacités de chiffrement intégrées de la base de données pour garantir la sécurité et la conformité.
Réduit les préoccupations liées à la sécurité lors du stockage de données sensibles dans ApsaraDB for MongoDB.
MongoDB Community Edition ne prend pas en charge le chiffrement automatique, ce qui la distingue de l'édition Enterprise (Atlas).
Pour plus d'informations sur les versions des pilotes et les versions de base de données chiffrées, consultez Compatibilité de Queryable Encryption.
Limites
-
Les commandes de diagnostic et les journaux de requête sur une collection chiffrée sont expurgés ou masqués, ce qui limite l'analyse des problèmes :
Les commandes applicables à une collection chiffrée, telles que
aggregate, count, find, insert, update, and delete, ne sont pas enregistrées dans les journaux de requêtes lentes ni dans les profileurs.Les résultats des commandes de diagnostic telles que
collStats, currentOp, top, or $planCacheStatssont expurgés et certains champs sont masqués.
La contention entre les champs chiffrés peut augmenter la latence d'écriture. Le facteur de contention par défaut est de 8.
Les collections de métadonnées dont la taille dépasse 1 Go doivent être compactées manuellement. Pour plus d'informations, consultez Compaction des collections de métadonnées.
L'objet
encryptedFieldsMapne peut pas être modifié, y compris les champs de requête dans l'objet.Queryable Encryption est pris en charge uniquement par les instances de replica set ou de sharded cluster.
Les données chiffrées sur les nœuds secondaires ne peuvent pas être lues.
Les documents ne peuvent pas être mis à jour par lot en exécutant la commande
updateMany or bulkWriteet les paramètres de la commandefindAndModifysont limités.L'opération upsert n'est pas prise en charge. Lorsque l'upsert est déclenché, les champs chiffrés ne sont pas insérés.
Client-Side Field Level Encryption (CSFLE) et Queryable Encryption ne peuvent pas être activés sur la même collection. Une collection avec CSFLE activé ou une collection non chiffrée ne peut pas être convertie en collection Queryable Encryption.
Queryable Encryption ne peut être activé que sur de nouvelles collections vides.
Une collection contenant des champs chiffrés ne peut pas être renommée et les champs ne peuvent pas être renommés en exécutant la commande
$rename.Si
jsonSchemaest spécifié lors de la création d'une collection chiffrée, le mot-cléencryptne peut pas être inclus.Les vues, les collections de séries temporelles et les collections plafonnées (capped collections) ne sont pas prises en charge.
Les index TTL ou les index uniques ne sont pas pris en charge.
jsonSchemane peut pas être désactivé.Une collection doit être supprimée à l'aide d'un MongoClient avec Queryable Encryption activé. Sinon, les métadonnées restent sur le serveur.
Queryable Encryption ne prend pas en charge la collation. La collation bloque le tri normal pour les champs chiffrés.
Le champ
_idne peut pas être spécifié comme champ chiffré.Seul un nombre limité de commandes et d'opérateurs est pris en charge. Pour plus d'informations, consultez Opérations prises en charge pour Queryable Encryption.
Préparatifs
Ce guide utilise une instance ECS en tant que client pour illustrer la procédure. Si votre environnement de test dispose déjà des dépendances nécessaires, vous pouvez ignorer les étapes correspondantes. Ce guide utilise le pilote Node.js pour la démonstration, car mongosh ne prend en charge que le chiffrement automatique, tandis que MongoDB Community Edition ne prend en charge que le chiffrement explicite.
-
Installez Node.js et npm.
curl -fsSL https://rpm.nodesource.com/setup_lts.x | sudo bash - sudo yum install nodejs node -v npm -v -
Installez le pilote Node.js officiel pour MongoDB.
mkdir node_quickstart cd node_quickstart npm init -y npm install mongodb@6.6 -
Installez la bibliothèque libmongocrypt.
vi /etc/yum.repos.d/libmongocrypt.repo // Add the following content to 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 the library sudo yum install -y libmongocrypt -
Installez le package mongodb-client-encryption dont dépend le pilote Node.js.
sudo yum groupinstall 'Development Tools' npm install mongodb-client-encryption -
Installez mongosh et configurez la variable d'environnement 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 connection mongosh ${MONGODB_URI} -
Obtenez la bibliothèque partagée de chiffrement automatique.
Sélectionnez le client correspondant à votre machine et à la version de votre distribution dans le centre de téléchargement, puis sélectionnez le package crypt_shared. Pour plus d'informations, consultez Téléchargement de 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
Procédure
MongoDB Community Edition ne prend pas en charge le chiffrement automatique. Par conséquent, cet article illustre le processus de chiffrement explicite.
Accédez à l'environnement REPL Node.js, puis effectuez les étapes suivantes :
node -i -e "const MongoClient = require('mongodb').MongoClient; const ClientEncryption = require('mongodb').ClientEncryption;"
-
Créez une clé maître client (CMK).
RemarqueL'exemple suivant montre les configurations types d'un fournisseur KMS local. Nous vous déconseillons d'utiliser ces configurations dans un environnement de production.
Créez une CMK de 96 octets et stockez-la dans le fichier
customer-master-key.txtdu système de fichiers local.const fs = require("fs"); const crypto = require("crypto"); try { fs.writeFileSync("customer-master-key.txt", crypto.randomBytes(96)); } catch (err) { console.error(err); }Cet exemple génère la CMK à l'aide d'un appel crypto Node.js. Vous pouvez également générer la CMK de 96 octets dans un shell en utilisant
/dev/urandom:echo $(head -c 96 /dev/urandom | base64 | tr -d '\n') -
Initialisez les variables.
// 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";Dans l'exemple de code précédent, les variables suivantes sont initialisées :
kmsProviderName: le nom du fournisseur KMS. Dans cet exemple,localest utilisé.uri: l'URI MongoDB. L'URI MongoDB peut être spécifié par la variable d'environnementMONGODB_URI. Vous pouvez également spécifier l'URI MongoDB directement.keyVaultDatabaseName: le nom de la base de données qui stocke les clés de chiffrement de données (DEK).keyVaultCollectionName: le nom de la collection qui stocke les DEK. La collection doit être différente d'une collection standard.keyVaultNamespace: équivalent à la variablekeyVaultDatabaseNameoukeyVaultCollectionName.encryptedDatabaseName: le nom de la base de données qui stocke les données chiffrées.encryptedCollectionName: le nom de la collection qui stocke les données chiffrées.
-
Créez un index unique sur la collection qui stocke les 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(); -
Créez une collection chiffrée.
-
Récupérez la CMK créée et spécifiez le fournisseur KMS.
const localMasterKey = fs.readFileSync("./customer-master-key.txt"); kmsProviders = {local: {key: localMasterKey}}; -
Créez une DEK.
RemarqueAvant d'effectuer cette étape, assurez-vous que l'utilisateur spécifié dans la variable
uridispose de l'autorisation dbAdmin sur les bases de donnéesencryption._keyVaultetmedicalRecords.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"], }); -
Spécifiez les champs à chiffrer et configurez la DEK créée.
const encryptedFieldsMap = { [`${encryptedDatabaseName}.${encryptedCollectionName}`]: { fields: [ { keyId: dek1, path: "patientId", bsonType: "int", queries: { queryType: "equality" }, }, { keyId: dek2, path: "medications", bsonType: "array", }, ], }, }; -
Spécifiez la bibliothèque partagée de chiffrement automatique et créez un MongoClient.
const extraOptions = {cryptSharedLibPath: "/root/lib/mongo_crypt_v1.so"}; const encClient = new MongoClient(uri, { autoEncryption: { keyVaultNamespace, kmsProviders, extraOptions, encryptedFieldsMap, }, }); await encClient.connect(); -
Créez une collection chiffrée.
const newEncDB = encClient.db(encryptedDatabaseName); await newEncDB.dropDatabase(); await newEncDB.createCollection(encryptedCollectionName);
-
-
Créez un MongoClient utilisé pour chiffrer les opérations de lecture et d'écriture.
-
Spécifiez la collection qui stocke la DEK créée.
const eDB = "encryption"; const eKV = "__keyVault"; const keyVaultNamespace = `${eDB}.${eKV}`; const secretDB = "medicalRecords"; const secretCollection = "patients"; -
Spécifiez la CMK créée.
ImportantN'utilisez pas le fichier de clé local dans un environnement de production.
const fs = require("fs"); const path = "./customer-master-key.txt"; const localMasterKey = fs.readFileSync(path); const kmsProviders = { local: { key: localMasterKey, }, }; -
Récupérez la DEK créée.
RemarqueLe nom de la DEK doit être identique au nom de la DEK créée dans la sous-étape secondaire de l'étape 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" }); -
Spécifiez la bibliothèque partagée de chiffrement automatique et créez un 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(); -
Créez un objet ClientEncryption.
const encryption = new ClientEncryption(unencryptedClient, { keyVaultNamespace, kmsProviders, });
-
-
Insérez un document contenant des champs chiffrés dans la collection chiffrée créée.
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, }); -
Effectuez une requête au niveau du champ sur la collection chiffrée créée.
const findPayload = await encryption.encrypt(patientId, { algorithm: "Indexed", keyId: dek1._id, queryType: "equality", contentionFactor: 1, }); console.log(await encryptedColl.findOne({ patientId: findPayload }));L'exemple suivant montre le document renvoyé.
> console.log(await encryptedColl.findOne({ patientId: findPayload })); { _id: new ObjectId('6645b56f58abf955ebd95caf'), firstName: 'Jon', patientId: 12345678, medications: [ 'Atorvastatin', 'Levothyroxine' ], __safeContent__: [ Binary.createFromBase64('IrPf972hlhDvnasQH6rIAW6BqERo0ZEgC6C0/zNQiIY=', 0) ] } -
Utilisez le client contenant les options de chiffrement pour accéder aux champs chiffrés. Sinon, les champs chiffrés ne seront pas accessibles.
Utilisez le client
unencryptedClientqui n'est pas chiffré pour la requête au niveau du champ.console.log(await unencryptedClient.db(secretDB).collection(secretCollection).findOne());L'exemple suivant montre le document renvoyé. Les champs sensibles apparaissent sous forme de données binaires.
> 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) ] }Vous pouvez également utiliser mongosh pour accéder aux champs chiffrés de manière externe. Cela simule l'accès à la collection chiffrée créée sans la clé cliente.
// In a separate terminal session, connect directly to the MongoDB URI with mongosh. mongosh ${MONGODB_URI} db.getSiblingDB("medicalRecords").patients.findOne()L'exemple suivant montre le document renvoyé.
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) ] }