Overview
在本指南中,您可以学习;了解如何创建使用Mongoose的应用程序来实现MongoDB 的Queryable Encryption功能。
Queryable Encryption允许您自动加密和解密文档字段。您可以使用Queryable Encryption来加密应用程序中的敏感数据,将数据字段作为随机加密数据存储在服务器上,并查询加密字段。本教程向您展示如何使用Mongoose设立Queryable Encryption ,Mongoose 为数据交互提供了对象文档映射器 (ODM) 库。
先决条件
在开始本教程之前,请完成以下先决任务:
创建MongoDB Atlas帐户并配置集群。确保您的集群在MongoDB Server7.0 或更高版本上运行。要学习;了解更多信息,请参阅 MongoDB入门指南。
下载自动加密共享库。要查看说明,请参阅《安装和配置查询分析组件》指南。这些说明展示了如何导航到MongoDB下载中心并填写下载库所需的表格。
安装 Node.js v..16 201或更高版本。
Tutorial
本教程介绍如何创建使用Mongoose 的Queryable Encryption应用程序。该应用程序对患者病历进行加密和解密,并查询加密的医疗数据。
设置您的项目
按照本节中的步骤安装项目依赖项、配置环境并创建应用程序结构。
设置您的环境。
在终端中运行以下命令以初始化项目并安装必要的依赖项:
mkdir mongoose-qe-app cd mongoose-qe-app npm init -y npm pkg set main="queryable-encryption-tutorial.js" npm pkg set type="module" npm pkg set scripts.start="node queryable-encryption-tutorial.js" npm i mongoose mongodb dotenv mongodb-client-encryption
这些命令创建一个 mongoose-qe-app项目目录并安装以下依赖项:
Mongoose,Node.js ODM
Node.js驾驶员
指定环境变量。
在项目根目录中,创建一个 .env文件并粘贴以下代码:
# MongoDB Connection URI and Shared Library Path MONGODB_URI="<connection URI>" SHARED_LIB_PATH="<Automatic Encryption Shared Library path>" # AWS Credentials AWS_ACCESS_KEY_ID="<Your AWS access key ID>" AWS_SECRET_ACCESS_KEY="<Your AWS secret access key>" AWS_KEY_REGION="<Your AWS key region>" AWS_KEY_ARN="<Your AWS key ARN>" # Azure Credentials AZURE_TENANT_ID="<Your Azure tenant ID>" AZURE_CLIENT_ID="<Your Azure client ID>" AZURE_CLIENT_SECRET="<Your Azure client secret>" AZURE_KEY_NAME="<Your Azure key name>" AZURE_KEY_VERSION="<Your Azure key version>" AZURE_KEY_VAULT_ENDPOINT="<Your Azure key vault endpoint>" # GCP Credentials GCP_EMAIL="<Your GCP email>" GCP_PRIVATE_KEY="<Your GCP private key>" GCP_PROJECT_ID="<Your GCP project ID>" GCP_LOCATION="<Your GCP location>" GCP_KEY_RING="<Your GCP key ring>" GCP_KEY_NAME="<Your GCP key name>" GCP_KEY_VERSION="<Your GCP key version>" # KMIP Credentials KMIP_KMS_ENDPOINT="<Endpoint for your KMIP KMS>" KMIP_TLS_CA_FILE="<Full path to your KMIP certificate authority file>" KMIP_TLS_CERT_FILE="<Full path to your client certificate file>"
将 <connection URI> 占位符替换为连接到集群的连接 URI,并将 <Automatic Encryption Shared Library path> 替换为自动加密共享库的完整路径。确保该路径指向下载的包中的 lib/mongo_crypt_v1.dylib。
如果您使用 .env 模板中列出的密钥管理系统 (KMS) 之一,请替换相应的占位符值。否则,您可以保留这些值未分配并在本地存储您的客户主密钥。此应用程序为您创建本地客户主密钥文件。
警告
本地集合扫描存储
如果您将客户主密钥存储在本地,请勿在生产中使用此应用程序。如果没有远程KMS,您可能会面临未经授权访问权限加密密钥或丢失解密数据所需密钥的风险。
创建应用程序文件。
导航到 mongoose-qe-app目录并创建名为 queryable-encryption-tutorial.js 的文件,该文件将存储应用程序逻辑。将以下代码粘贴到此文件中:
import 'dotenv/config'; import mongoose from 'mongoose'; import * as qeHelper from './queryable-encryption-helpers.js'; import { MongoClient, ClientEncryption } from 'mongodb'; async function runExample() { // Paste initial application variables below // Paste credential and options variables below // Paste connection and client configuration below // Paste data key creation code below // Paste encryption schema below // Paste the model below // Paste connection code below // Paste the insertion operation below // Paste the encrypted query below await connection.close(); console.log('Connection closed.'); } runExample().catch(console.dir);
然后,创建一个名为 queryable-encryption-helpers.js 的文件并粘贴以下代码:
import 'dotenv/config'; import { writeFileSync, readFileSync, existsSync } from 'fs'; import { randomBytes } from 'crypto'; export async function dropExistingDatabase(client, databaseName) { const database = client.db(databaseName); await database.dropDatabase(); } // Paste helper methods below
此文件包含应用程序的 dropExistingDatabase() 辅助函数。本教程的后续步骤将指导您添加其他辅助函数。
分配应用程序变量。
通过将以下代码粘贴到 queryable-encryption-tutorial.js文件的 runExample() 函数中,指定初始数据库和加密变量:
const kmsProviderName = '<KMS provider>'; const uri = process.env.MONGODB_URI; // Your connection URI const keyVaultDatabaseName = 'encryption'; const keyVaultCollectionName = '__keyVault'; const keyVaultNamespace = `${keyVaultDatabaseName}.${keyVaultCollectionName}`; const encryptedDatabaseName = 'medicalRecords'; const encryptedCollectionName = 'patients';
将 '<KMS provider>' 占位符替换为您的密钥提供商:'aws'、'azure'、'gcp' 或 'kmip'。要在本地存储客户主密钥,请将此值设立为 'local'。
该代码预填充了以下变量:
keyVaultDatabaseName - MongoDB中存储数据加密密钥 (DEK) 的数据库。本教程使用
encryption数据库。keyVaultCollectionName - MongoDB中存储 DEK 的集合。代码将此变量设置为
__keyVault,以下划线为前缀,以将其与用户集合分开来。keyVaultNamespace - MongoDB中用于存储 DEK 的命名空间。该变量由
keyVaultDatabaseName和keyVaultCollectionName变量组成,各变量之间用句点分隔。cryptoDatabaseName - MongoDB中存储加密数据的数据库。本教程使用
medicalRecords数据库。cryptoCollectionName - MongoDB中存储加密数据的集合。本教程使用
patients集合。
配置加密凭证
设置项目结构后,请按照本节中的步骤配置KMS提供商凭证和加密选项。
检索您的KMS提供商凭证。
将 getKMSProviderCredentials() 函数添加到 queryable-encryption-helpers.js文件中。该函数检索密钥管理系统提供商的凭证,如果不使用KMS提供商,则会创建本地客户主密钥文件。
在 dropExistingDatabase() 函数之后粘贴以下代码:
export function getKMSProviderCredentials(kmsProviderName) { let kmsProviders; switch (kmsProviderName) { case 'aws': kmsProviders = { aws: { accessKeyId: process.env.AWS_ACCESS_KEY_ID, // Your AWS access key ID secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, // Your AWS secret access key }, }; return kmsProviders; case 'azure': kmsProviders = { azure: { tenantId: process.env.AZURE_TENANT_ID, // Your Azure tenant ID clientId: process.env.AZURE_CLIENT_ID, // Your Azure client ID clientSecret: process.env.AZURE_CLIENT_SECRET, // Your Azure client secret }, }; return kmsProviders; case 'gcp': kmsProviders = { gcp: { email: process.env.GCP_EMAIL, // Your GCP email privateKey: process.env.GCP_PRIVATE_KEY, // Your GCP private key }, }; return kmsProviders; case 'kmip': kmsProviders = { kmip: { endpoint: process.env.KMIP_KMS_ENDPOINT, // Your KMIP KMS endpoint }, }; return kmsProviders; case 'local': (function () { if (!existsSync('./customer-master-key.txt')) { try { writeFileSync('customer-master-key.txt', randomBytes(96)); } catch (err) { throw new Error( `Unable to write Customer Master Key to file due to the following error: ${err}` ); } } })(); try { // WARNING: Do not use a local key file in a production application const localMasterKey = readFileSync('./customer-master-key.txt'); if (localMasterKey.length !== 96) { throw new Error( 'Expected the customer master key file to be 96 bytes.' ); } kmsProviders = { local: { key: localMasterKey, }, }; } catch (err) { throw new Error( `Unable to read the Customer Master Key due to the following error: ${err}` ); } return kmsProviders; default: throw new Error( `Unrecognized value for KMS provider name \'${kmsProviderName}\' encountered while retrieving KMS credentials.` ); } }
此 getKMSProviderCredentials() 函数支持多个KMS提供程序,包括 AWS、 Azure、 GCP、KMIP 和本地密钥存储。
检索您的客户主密钥凭证。
将 getCustomerMasterKeyCredentials() 函数添加到 queryable-encryption-helpers.js文件中。此函数根据您的KMS提供商检索客户主密钥凭证。
在 getKMSProviderCredentials() 函数之后粘贴以下代码:
export function getCustomerMasterKeyCredentials(kmsProviderName) { let customerMasterKeyCredentials; switch (kmsProviderName) { case 'aws': customerMasterKeyCredentials = { key: process.env.AWS_KEY_ARN, // Your AWS Key ARN region: process.env.AWS_KEY_REGION, // Your AWS Key Region }; return customerMasterKeyCredentials; case 'azure': customerMasterKeyCredentials = { keyVaultEndpoint: process.env.AZURE_KEY_VAULT_ENDPOINT, // Your Azure Key Vault Endpoint keyName: process.env.AZURE_KEY_NAME, // Your Azure Key Name }; return customerMasterKeyCredentials; case 'gcp': customerMasterKeyCredentials = { projectId: process.env.GCP_PROJECT_ID, // Your GCP Project ID location: process.env.GCP_LOCATION, // Your GCP Key Location keyRing: process.env.GCP_KEY_RING, // Your GCP Key Ring keyName: process.env.GCP_KEY_NAME, // Your GCP Key Name }; return customerMasterKeyCredentials; case 'kmip': case 'local': customerMasterKeyCredentials = {}; return customerMasterKeyCredentials; default: throw new Error( `Unrecognized value for KMS provider name \'${kmsProviderName}\' encountered while retrieving Customer Master Key credentials.` ); } }
此函数为所选KMS提供商配置相应的客户主密钥凭证。
检索自动加密选项。
将 getAutoEncryptionOptions() 函数添加到 queryable-encryption-helpers.js文件中。该函数为您的应用程序配置自动加密选项。
在 getCustomerMasterKeyCredentials() 函数之后粘贴以下代码:
export async function getAutoEncryptionOptions( kmsProviderName, keyVaultNamespace, kmsProviders ) { if (kmsProviderName === 'kmip') { const tlsOptions = { kmip: { tlsCAFile: process.env.KMIP_TLS_CA_FILE, // Path to your TLS CA file tlsCertificateKeyFile: process.env.KMIP_TLS_CERT_FILE, // Path to your TLS certificate key file }, }; const extraOptions = { cryptSharedLibPath: process.env.SHARED_LIB_PATH, // Path to your Automatic Encryption Shared Library }; const autoEncryptionOptions = { keyVaultNamespace, kmsProviders, extraOptions, tlsOptions, }; return autoEncryptionOptions; } else { const extraOptions = { cryptSharedLibPath: process.env.SHARED_LIB_PATH, // Path to your Automatic Encryption Shared Library }; const autoEncryptionOptions = { keyVaultNamespace, kmsProviders, extraOptions, }; return autoEncryptionOptions; } }
分配凭证变量。
现在您已经定义了辅助函数,您可以使用它们从主应用程序文件访问权限您的凭证。
导航到 queryable-encryption-tutorial.js文件,将以下代码粘贴到 // Paste credential and options variables below 代码注释之后:
const kmsProviderCredentials = qeHelper.getKMSProviderCredentials(kmsProviderName); const customerMasterKeyCredentials = qeHelper.getCustomerMasterKeyCredentials(kmsProviderName); const autoEncryptionOptions = await qeHelper.getAutoEncryptionOptions( kmsProviderName, keyVaultNamespace, kmsProviderCredentials );
此代码调用辅助函数来检索KMS提供商凭证、客户主密钥凭证以及用于配置Queryable Encryption 的自动加密选项。
加密并连接到MongoDB
配置加密设置后,请按照本节中的步骤设立MongoDB连接、创建数据密钥并定义加密模式。
配置MongoDB连接和客户端。
要设立MongoDB连接和客户端,请导航到 queryable-encryption-tutorial.js文件并在 // Paste connection and client configuration below 代码注释之后粘贴以下代码:
const connection = mongoose.createConnection(); const client = new MongoClient(uri); const clientEncryption = new ClientEncryption( client, autoEncryptionOptions ); await qeHelper.dropExistingDatabase(client, encryptedDatabaseName); await qeHelper.dropExistingDatabase(client, keyVaultDatabaseName);
此代码创建用于加密操作的Mongoose连接、用于密钥管理的MongoDB客户端以及用于创建数据密钥的 ClientEncryption实例。它还会删除所有现有数据库,以确保对本教程进行全新设置。
创建数据密钥。
要创建必要的数据键,请将以下代码粘贴到 // Paste data key creation code below 代码注释之后:
const keyId1 = await clientEncryption.createDataKey( kmsProviderName, { masterKey: customerMasterKeyCredentials, }); const keyId2 = await clientEncryption.createDataKey( kmsProviderName, { masterKey: customerMasterKeyCredentials, }); const keyId3 = await clientEncryption.createDataKey( kmsProviderName, { masterKey: customerMasterKeyCredentials, });
此代码创建三个数据加密密钥,用于加密患者文档中的不同字段。每个加密字段都需要自己的数据密钥。
定义加密模式。
要创建加密模式定义,请将以下代码粘贴到 // Paste encryption schema below 代码注释之后:
const patientSchema = new mongoose.Schema({ patientName: { type: String, required: true }, patientId: { type: Number, required: true }, patientRecord: { ssn: { type: String, encrypt: { keyId: keyId1, queries: { queryType: 'equality' } } }, billing: { type: { type: String, encrypt: { keyId: keyId2, } }, number: { type: String, encrypt: { keyId: keyId3 } } }, billAmount: Number } }, { encryptionType: 'queryableEncryption', collection: encryptedCollectionName });
此模式定义了 patients集合中文档的结构,并指定了以下加密字段:
patientRecord.ssn:针对相等查询进行加密和配置patientRecord.billing.type:已加密,但不可查询patientRecord.billing.number:已加密,但不可查询
执行加密操作
配置应用程序和数据库连接后,请按照本节中的步骤插入和查询加密文档。
插入加密数据。
要插入具有加密字段的文档,请导航到 queryable-encryption-tutorial.js文件并将以下代码粘贴到 // Paste the insertion operation below 代码注释之后:
const patientDocument = { patientName: 'Jon Doe', patientId: 12345678, patientRecord: { ssn: '987-65-4320', billing: { type: 'Visa', number: '4111111111111111', }, billAmount: 1500, }, }; const result = await Patient.create(patientDocument); if (result) { console.log('Successfully inserted the patient document.'); console.log('Document ID:', result._id); }
在将文档存储到MongoDB之前,Queryable Encryption自动加密 patientRecord.ssn 和 patientRecord.billing 字段。
运行您的应用程序。
最后,您可以通过从 mongoose-qe-app目录运行以下命令来运行前面步骤中定义的加密操作:
npm start
如果成功,命令输出将类似于以下示例:
Successfully inserted the patient document. Document ID: new ObjectId('...') Found patient: { patientRecord: { billing: { type: 'Visa', number: '4111111111111111' }, ssn: '987-65-4320', billAmount: 1500 }, _id: new ObjectId('...'), patientName: 'Jon Doe', patientId: 12345678, __v: 0, __safeContent__: [ Binary.createFromBase64('EGzhQwBpf1B6W9udskSxJ8kwEEnF5P+SJPZ6ygQ9Ft8=', 0) ] } Connection closed.
后续步骤
恭喜完成Mongoose Queryable Encryption教程!您现在拥有一个Mongoose示例应用程序,它使用Queryable Encryption自动加密和解密文档字段。您的应用程序会在服务器端加密敏感数据,并在客户端端查询数据。
要学习;了解有关Queryable Encryption和Mongoose 的更多信息,请访问以下资源:
在MongoDB Server手册中查看更多驾驶员Queryable Encryption教程。
在 Mongoose入门教程中了解如何创建使用Mongoose而不使用Queryable Encryption的应用程序。