对于 AI 代理:可在 https://www.mongodb.com/zh-cn/docs/llms.txt 获取文档索引—通过在任何 URL 路径后添加 .md 可获取所有页面的 Markdown 版本。

教程:使用Mongoose的Queryable Encryption

在本指南中,您可以学习如何创建使用Mongoose的应用程序来实现MongoDB的Queryable Encryption功能。

Queryable Encryption允许您自动加密和解密文档字段。您可以使用Queryable Encryption来加密应用程序中的敏感数据,将数据字段作为随机加密数据存储在服务器上,并查询加密字段。本教程向您展示如何使用Mongoose设立Queryable Encryption ,Mongoose 为数据交互提供了对象文档映射器 (ODM) 库。

在开始本教程之前,请完成以下先决任务:

  • 创建MongoDB Atlas帐户并配置集群。确保您的集群在MongoDB Server 7.0 或更高版本上运行。要学习;了解更多信息,请参阅MongoDB入门指南。

  • 下载自动加密共享库。要查看说明,请参阅《安装和配置查询分析组件》指南。这些说明展示了如何导航到MongoDB下载中心并填写下载该库所需的表格。

  • 安装 Node.js 16.20.1 版本或更高版本。

本教程介绍如何创建使用Mongoose 的Queryable Encryption应用程序。该应用程序对患者病历进行加密和解密,并查询加密的医疗数据。

提示

完成应用程序

要查看本教程的完整示例应用程序,请参阅 GitHub 上的 mongoose-qe-app 文件夹。

按照本节中的步骤安装项目依赖项、配置环境并创建应用程序结构。

1

在终端中运行以下命令以初始化项目并安装必要的依赖项:

mkdir mongoose-qe-appcd mongoose-qe-appnpm init -ynpm 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项目目录并安装以下依赖项:

2

在项目根目录中,创建一个 .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,您可能会面临未经授权访问权限加密密钥或丢失解密数据所需密钥的风险。

提示

密钥管理系统

要学习有关KMS 的更多信息,请参阅“密钥管理”维基百科条目。

3

导航到 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() 辅助工具函数。本教程的后续步骤将指导您添加其他辅助程序。

4

通过将以下代码粘贴到 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 变量组成,各变量之间用句点分隔。

  • encryptedDatabaseName - MongoDB中存储加密数据的数据库。本教程使用 medicalRecords数据库。

  • cryptoCollectionName - MongoDB中存储加密数据的集合。本教程使用 patients集合。

设置项目结构后,请按照本节中的步骤配置KMS提供商凭证和加密选项。

1

将 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 和本地密钥存储。

2

将 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提供商配置相应的客户主密钥凭证。

3

将 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; }}
4

现在您已经定义了辅助函数,您可以使用它们从主应用程序文件访问权限您的凭证。

导航到 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连接、创建数据密钥并定义加密模式。

1

要设立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实例。它还会删除所有现有数据库,以确保对本教程进行全新设置。

2

要创建必要的数据键,请将以下代码粘贴到 // 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,});

此代码创建三个数据加密密钥,用于加密患者文档中的不同字段。每个加密字段都需要自己的数据密钥。

3

要创建加密模式定义,请将以下代码粘贴到 // 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:已加密,但不可查询

4

创建一个名为 Patient 的新模型来表示 patients集合,并在该模型上注册您的加密模式。要创建模型,请将以下代码粘贴到 // Paste the model below 代码注释之后:

const Patient = connection.model('Patient', patientSchema);

这段代码创建了一个Mongoose模型,当您执行数据库操作时,该模型会处理字段的自动加密和解密。

5

注册模型后,可以通过在 // Paste connection code below 代码注释后粘贴以下代码来建立数据库连接:

await connection.openUri(uri, { autoEncryption: autoEncryptionOptions, dbName: encryptedDatabaseName});

这将建立与 medicalRecords数据库的连接,并在该数据库上启用自动加密。

配置应用程序和数据库连接后,请按照本节中的步骤插入和查询加密文档。

1

要插入具有加密字段的文档,请导航到 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 字段。

2

要查询加密字段,请将以下代码粘贴到 // Paste the encrypted query below 代码注释之后:

const findResult = await Patient.findOne({ 'patientRecord.ssn': '987-65-4320',});console.log('Found patient:');console.log(findResult);

此代码对加密的patientRecord.ssn字段执行相等查询。

3

最后,您可以通过从 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的应用程序。

  • 在Mongoose文档中了解详情有关Mongoose 的更多信息。