Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Junte-se a nós no Amazon Web Services re:Invent 2024! Saiba como usar o MongoDB para casos de uso de AI .
Desenvolvedor do MongoDB
Central de desenvolvedor do MongoDBchevron-right
Idiomaschevron-right
JavaScriptchevron-right

Como usar a criptografia no nível do campo do lado do cliente (CSFLE) do MongoDB com Node.js

Joe Karlsson12 min read • Published Jan 10, 2022 • Updated Sep 23, 2022
Node.jsMongoDBSegurançaJavaScript
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Você já precisou desenvolver um aplicativo que armazena dados confidenciais, como números de cartão de crédito ou de previdência social? Esse é um caso de uso muito comum para bancos de dados, e pode ser difícil salvar esses dados de forma segura. Felizmente para nós, existem alguns recursos de segurança incríveis que vêm com o MongoDB. Por exemplo, você deve saber que, com o MongoDB, você pode aproveitar:
O diagrama a seguir é uma lista dos recursos de segurança do MongoDB oferecidos e as possíveis vulnerabilidades de segurança que eles abordam:
A criptografia no nível do campo no lado do cliente permite que os engenheiros especifiquem os campos de um documento que deve ser mantido criptografado. Os dados confidenciais são criptografados/descriptografados de forma transparente pelo cliente e só são comunicados de e para o servidor de forma criptografada. Esse mecanismo mantém os campos de dados especificados seguros de forma criptografada no servidor e na rede. Embora todos os clientes tenham acesso aos campos de dados não confidenciais, somente os clientes CSFLE configurados adequadamente podem ler e gravar os campos de dados confidenciais.
Nesta postagem, projetaremos um cliente Node.js que pode ser usado para armazenar com segurança campos selecionados como parte de um aplicativo médico.

Os requisitos

Existem alguns requisitos que devem ser atendidos antes de tentar usar a criptografia no nível do campo do lado do cliente (CSFLE) com o driver Node.js.
Este tutorial se concentrará na criptografia automática. Embora este tutorial use o MongoDB Atlas, você precisará usar a versão 4.2 ou mais recente para MongoDB Atlas ou MongoDB Enterprise Edition. Você não poderá usar criptografia automática em nível de campo com o MongoDB Community Edition.
A suposição é que você esteja familiarizado com o desenvolvimento de aplicativos Node.js que usam MongoDB. Se quiser se atualizar, dê uma olhada na série de início rápido que publicamos sobre o tópico.

Instalando os binários e bibliotecas libmongocrypt e Mongocryptd

Devido aos requisitosdo libmongocrypt e do mongocryptd, vale a pena revisar como instalá-los e configurá-los. Exploraremos a instalação no macOS, mas consulte a documentação de libmongocrypt e mongocryptd para seu sistema operacional específico.

libmongocrypt

A libmongocrypt é necessária para a criptografia automática em nível de campo, pois é o componente responsável por executar a criptografia ou descriptografia dos dados no cliente com o MongoDB 4.2-drivers de nó compatíveis. Agora, atualmente existem algumas soluções para instalar a bibliotecalibmongocrypt no macOS. No entanto, o mais fácil é com Homebrew. Se você tiver o Homebrew instalado, poderá instalar alibmongocrypt com o seguinte comando:
1brew install mongodb/brew/libmongocrypt
Tive um problema com o libmongocrypt quando tentei executar meu código, porque o libmongocrypt estava tentando se vincular estaticamente ao libmongocrypt em vez de se vincular dinamicamente. Enviei um problema para a equipe para corrigir esse problema, mas, para corrigi-lo, tive que correr:
1export BUILD_TYPE=dynamic

mongocryptd

O mongocryptd é necessário para a criptografia automática em nível de campo e está incluído como componente no pacote MongoDB Enterprise Server . Omongocryptd é responsável apenas por oferecer suporte à criptografia automática no nível do campo no lado do cliente e não executa criptografia ou descriptografia.
Consulte a documentação sobre como obter o bináriomongocryptd, pois cada sistema operacional tem etapas diferentes.
Para macOS, você deve baixar o MongoDB Enterprise Edition no Centro de Download do MongoDB . Você pode consultar as instruções de instalação da Enterprise Edition para instalar o macOS, mas a essência da instalação envolve extrair o arquivo TAR e movê-los para o diretório apropriado .
Nesse ponto, todos os componentes apropriados para criptografia em nível de campo do lado do cliente devem estar instalados ou disponíveis. Certifique-se de que você esteja executando o MongoDB Enterprise em seu cliente enquanto estiver usando o CSFLE, mesmo se estiver salvando seus dados no Atlas.

Configuração do projeto

Vamos começar configurando todos os arquivos e dependências de que precisaremos. Em um novo diretório, crie os seguintes arquivos, executando o seguinte comando:
1touch clients.js helpers.js make-data-key.js
Certifique-se de inicializar um novo projeto NPM, pois usaremos várias dependências NPM.
1npm init --yes
E Go instalar todos os pacotes que usaremos agora.
1npm install -S mongodb mongodb-client-encryption node-gyp
Observação: a base de código completa para este projeto pode ser encontrada aqui: https://github.com/JoeKarlson/client-side-field-level-encryption-csfle-mongodb-node-demo

Criar uma chave de dados no MongoDB para criptografar e descriptografar campos de documento

A CSFLE (Client-Side Field Level Encryption) do MongoDB usa uma estratégia de criptografia chamada criptografia de envelope, na qual as chaves usadas para criptografar/descriptografar dados (chamadas de chaves de criptografia de dados) são criptografadas com outra chave (chamada de chave mestra). O diagrama a seguir mostra como a chave mestra é criada e armazenada:
Diagrama que descreve a criação da chave mestre ao usar um fornecedor local
Diagrama que descreve a criação da chave mestre ao usar um fornecedor local
Aviso
O provedor de chaves locais não é adequado para produção.
O provedor de chaves local é um método inseguro de armazenamento e, portanto, não é recomendado se você planeja usar o CSFLE na produção. Em vez disso, configure uma chave mestra em um sistema de gerenciamento de chaves (KMS) que armazene e descriptografe as chaves de criptografia de dados remotamente.
Para saber como usar um KMS em sua implementação CSFLE, leia o guia Criptografia em nível de campo do lado do cliente: use um KMS para armazenar a chave mestra.
1// clients.js
2
3const fs = require("fs")
4const mongodb = require("mongodb")
5const { ClientEncryption } = require("mongodb-client-encryption")
6const { MongoClient, Binary } = mongodb
7
8module.exports = {
9readMasterKey: function (path = "./master-key.txt") {
10 return fs.readFileSync(path)
11},
12CsfleHelper: class {
13 constructor({
14 kmsProviders = null,
15 keyAltNames = "demo-data-key",
16 keyDB = "encryption",
17 keyColl = "__keyVault",
18 schema = null,
19 connectionString = "mongodb://localhost:27017",
20 mongocryptdBypassSpawn = false,
21 mongocryptdSpawnPath = "mongocryptd"
22 } = {}) {
23 if (kmsProviders === null) {
24 throw new Error("kmsProviders is required")
25 }
26 this.kmsProviders = kmsProviders
27 this.keyAltNames = keyAltNames
28 this.keyDB = keyDB
29 this.keyColl = keyColl
30 this.keyVaultNamespace = `${keyDB}.${keyColl}`
31 this.schema = schema
32 this.connectionString = connectionString
33 this.mongocryptdBypassSpawn = mongocryptdBypassSpawn
34 this.mongocryptdSpawnPath = mongocryptdSpawnPath
35 this.regularClient = null
36 this.csfleClient = null
37 }
38
39 /**
40 * In the guide, https://docs.mongodb.com/ecosystem/use-cases/client-side-field-level-encryption-guide/,
41 * we create the data key and then show that it is created by
42 * retreiving it using a findOne query. Here, in implementation, we only
43 * create the key if it doesn't already exist, ensuring we only have one
44 * local data key.
45 *
46 * @param {MongoClient} client
47 */
48 async findOrCreateDataKey(client) {
49 const encryption = new ClientEncryption(client, {
50 keyVaultNamespace: this.keyVaultNamespace,
51 kmsProviders: this.kmsProviders
52 })
53
54 await this.ensureUniqueIndexOnKeyVault(client)
55
56 let dataKey = await client
57 .db(this.keyDB)
58 .collection(this.keyColl)
59 .findOne({ keyAltNames: { $in: [this.keyAltNames] } })
60
61 if (dataKey === null) {
62 dataKey = await encryption.createDataKey("local", {
63 keyAltNames: [this.keyAltNames]
64 })
65 return dataKey.toString("base64")
66 }
67
68 return dataKey["_id"].toString("base64")
69 }
70}
O script a seguir gera uma chave mestra de 96-byte, gerenciada localmente, e a salva em um arquivo chamado master-key.txt no diretório a partir do qual o script é executado, além de salvá-la em nosso sistema improvisado de gerenciamento de chaves no Atlas.
1// make-data-key.js
2
3const { readMasterKey, CsfleHelper } = require("./helpers");
4const { connectionString } = require("./config");
5
6async function main() {
7const localMasterKey = readMasterKey()
8
9const csfleHelper = new CsfleHelper({
10 kmsProviders: {
11 local: {
12 key: localMasterKey
13 }
14 },
15 connectionString: "PASTE YOUR MONGODB ATLAS URI HERE"
16})
17
18const client = await csfleHelper.getRegularClient()
19
20const dataKey = await csfleHelper.findOrCreateDataKey(client)
21console.log("Base64 data key. Copy and paste this into clients.js\t", dataKey)
22
23client.close()
24}
25
26main().catch(console.dir)
Depois de salvar esse código, execute o seguinte para gerar e salvar nossas chaves.
1node make-data-key.js
E você deve obter essa saída no terminal. Certifique-se de salvar esta chave, pois a usaremos em nossa próxima etapa.
Também é uma boa ideia verificar se esses dados foram salvos corretamente. Go para seus clusters no Atlas e navegue até suas collection. Você deverá ver uma nova chave salva na collectionencryption.__keyVault.
Sua chave deve ter este formato:
1{
2 "_id": "UUID('27a51d69-809f-4cb9-ae15-d63f7eab1585')",
3 "keyAltNames": ["demo-data-key"],
4 "keyMaterial": "Binary('oJ6lEzjIEskH...', 0)",
5 "creationDate": "2020-11-05T23:32:26.466+00:00",
6 "updateDate": "2020-11-05T23:32:26.466+00:00",
7 "status": "0",
8 "masterKey": {
9 "provider": "local"
10 }
11}

Definir um mapa de JSON schema estendido para campos a serem criptografados

Com a chave de dados criada, estamos em um ponto no tempo em que precisamos descobrir quais campos devem ser criptografados em um documento e quais campos devem ser deixados como texto sem formatação. A maneira mais fácil de fazer isso é com um mapa de esquema.
Um mapa de esquema para criptografia é JSON estendido e pode ser adicionado diretamente ao código-fonte Go ou carregado de um arquivo externo. Do ponto de vista da manutenção, o carregamento de um arquivo externo é mais fácil de manter.
A tabela a seguir ilustra o modelo de dados do Sistema de Gerenciamento de Assistência Médica.
tipo de campoAlgoritmo de criptografiaTipo de JSON
NomeNão criptografadoString
SSNDeterminístico(a)Int
Tipo sanguíneoAleatórioString
Registros médicosAleatórioArray
Seguro: número da apóliceDeterminístico(a)Int (incorporado dentro do objeto de seguro)
Seguro: fornecedorNão criptografadostring (incorporado dentro do objeto de seguro)
Vamos adicionar uma função ao nosso métodoCSFLEhelper no arquivo helper.js para que nosso aplicativo saiba quais campos precisam ser criptografados e descriptografados .
1if (dataKey === null) {
2 throw new Error(
3 "dataKey is a required argument. Ensure you've defined it in clients.js"
4 )
5}
6return {
7 "medicalRecords.patients": {
8 bsonType: "object",
9 // specify the encryptMetadata key at the root level of the JSON Schema.
10 // As a result, all encrypted fields defined in the properties field of the
11 // schema will inherit this encryption key unless specifically overwritten.
12 encryptMetadata: {
13 keyId: [new Binary(Buffer.from(dataKey, "base64"), 4)]
14 },
15 properties: {
16 insurance: {
17 bsonType: "object",
18 properties: {
19 // The insurance.policyNumber field is embedded inside the insurance
20 // field and represents the patient's policy number.
21 // This policy number is a distinct and sensitive field.
22 policyNumber: {
23 encrypt: {
24 bsonType: "int",
25 algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
26 }
27 }
28 }
29 },
30 // The medicalRecords field is an array that contains a set of medical record documents.
31 // Each medical record document represents a separate visit and specifies information
32 // about the patient at that that time, such as their blood pressure, weight, and heart rate.
33 // This field is sensitive and should be encrypted.
34 medicalRecords: {
35 encrypt: {
36 bsonType: "array",
37 algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
38 }
39 },
40 // The bloodType field represents the patient's blood type.
41 // This field is sensitive and should be encrypted.
42 bloodType: {
43 encrypt: {
44 bsonType: "string",
45 algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
46 }
47 },
48 // The ssn field represents the patient's
49 // social security number. This field is
50 // sensitive and should be encrypted.
51 ssn: {
52 encrypt: {
53 bsonType: "int",
54 algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
55 }
56 }
57 }
58}

Crie o cliente MongoDB

Tudo bem, agora temos o JSON schema e as chaves de criptografia necessárias para criar um cliente MongoDB habilitado para CSFLE. Vamos recapitular como nosso cliente funcionará. Nosso cliente MongoDB habilitado para CSFLE consultará nossos dados criptografados e o processomongocryptd será iniciado automaticamente por padrão. O mongocryptd lida com as seguintes responsabilidades:
  • Valida as instruções de criptografia definidas no JSON schema e sinaliza os campos referenciados para criptografia em operações de leitura e gravação.
  • Impede que operações não suportadas sejam executadas em campos criptografados.
Para criar o cliente habilitado para CSFLE, precisamos instanciar um objeto cliente MongoDB padrão com as configurações adicionais de criptografia automática com o seguinte trecho de código:
1async getCsfleEnabledClient(schemaMap = null) {
2 if (schemaMap === null) {
3 throw new Error(
4 "schemaMap is a required argument. Build it using the CsfleHelper.createJsonSchemaMap method"
5 )
6 }
7 const client = new MongoClient(this.connectionString, {
8 useNewUrlParser: true,
9 useUnifiedTopology: true,
10 monitorCommands: true,
11 autoEncryption: {
12 // The key vault collection contains the data key that the client uses to encrypt and decrypt fields.
13 keyVaultNamespace: this.keyVaultNamespace,
14 // The client expects a key management system to store and provide the application's master encryption key.
15 // For now, we will use a local master key, so they use the local KMS provider.
16 kmsProviders: this.kmsProviders,
17 // The JSON Schema that we have defined doesn't explicitly specify the collection to which it applies.
18 // To assign the schema, they map it to the medicalRecords.patients collection namespace
19 schemaMap
20 }
21 })
22 return await client.connect()
23}
Se a conexão for bem-sucedida, o cliente é retornado.

Executar operações de leitura/gravação criptografadas

Agora temos um cliente habilitado para CSFLE e podemos testar se o cliente pode executar consultas que atendam aos nossos requisitos de segurança.

Inserir um documento com campos criptografados

O diagrama a seguir mostra as etapas seguidas pelo aplicativo cliente e pelo driver para executar uma gravação de dados criptografados em nível de campo:
Diagrama que mostra o fluxo de dados de uma gravação de dados criptografados em nível de campo
Diagrama que mostra o fluxo de dados de uma gravação de dados criptografados em nível de campo
Precisamos escrever uma função em nosso clients.js para criar um novo registro de paciente com o seguinte trecho de código:
Observação: os clientes que não têm o CSFLE configurado inserirão dados não criptografados. Recomendamos usar a validação de esquema do lado do servidor para impor gravações criptografadas para campos que devem ser criptografados.
1const { readMasterKey, CsfleHelper } = require("./helpers");
2const { connectionString, dataKey } = require("./config");
3
4const localMasterKey = readMasterKey()
5
6const csfleHelper = new CsfleHelper({
7 // The client expects a key management system to store and provide the application's master encryption key. For now, we will use a local master key, so they use the local KMS provider.
8 kmsProviders: {
9 local: {
10 key: localMasterKey
11 }
12 },
13 connectionString,
14})
15
16async function main() {
17let regularClient = await csfleHelper.getRegularClient()
18let schemeMap = csfleHelper.createJsonSchemaMap(dataKey)
19let csfleClient = await csfleHelper.getCsfleEnabledClient(schemeMap)
20
21let exampleDocument = {
22 name: "Jon Doe",
23 ssn: 241014209,
24 bloodType: "AB+",
25 medicalRecords: [
26 {
27 weight: 180,
28 bloodPressure: "120/80"
29 }
30 ],
31 insurance: {
32 provider: "MaestCare",
33 policyNumber: 123142
34 }
35}
36
37const regularClientPatientsColl = regularClient
38 .db("medicalRecords")
39 .collection("patients")
40const csfleClientPatientsColl = csfleClient
41 .db("medicalRecords")
42 .collection("patients")
43
44// Performs the insert operation with the csfle-enabled client
45// We're using an update with an upsert so that subsequent runs of this script
46// don't insert new documents
47await csfleClientPatientsColl.updateOne(
48 { ssn: exampleDocument["ssn"] },
49 { $set: exampleDocument },
50 { upsert: true }
51)
52
53// Performs a read using the encrypted client, querying on an encrypted field
54const csfleFindResult = await csfleClientPatientsColl.findOne({
55 ssn: exampleDocument["ssn"]
56})
57console.log(
58 "Document retreived with csfle enabled client:\n",
59 csfleFindResult
60)
61
62// Performs a read using the regular client. We must query on a field that is
63// not encrypted.
64// Try - query on the ssn field. What is returned?
65const regularFindResult = await regularClientPatientsColl.findOne({
66 name: "Jon Doe"
67})
68console.log("Document retreived with regular client:\n", regularFindResult)
69
70await regularClient.close()
71await csfleClient.close()
72}
73
74main().catch(console.dir)

Consulta documentos em um campo criptografado de forma determinística

O diagrama a seguir mostra as etapas realizadas pelo aplicativo cliente e pelo driver para consultar e descriptografar dados criptografados em nível de campo:
Podemos executar queries em documentos com campos criptografados usando métodos de driver padrão do MongoDB. Quando um médico realiza uma consulta no Medical Care Management System para procurar um paciente pelo seu SSN, o driver descriptografa os dados do paciente antes de retorná-los:
1{
2 "_id": "5d6ecdce70401f03b27448fc",
3 "name": "Jon Doe",
4 "ssn": 241014209,
5 "bloodType": "AB+",
6 "medicalRecords": [
7 {
8 "weight": 180,
9 "bloodPressure": "120/80"
10 }
11 ],
12 "insurance": {
13 "provider": "MaestCare",
14 "policyNumber": 123142
15 }
16}
Se você tentar consultar seus dados com um MongoDB que não esteja configurado com a chave correta, é isso que você verá:
E você deve ver seus dados gravados em seu banco de dados MongoDB Atlas:

Executando no Docker

Se você tiver problemas ao executar seu código localmente, desenvolvemos uma imagem do Docker que você pode usar para ajudá-lo a configurar rapidamente ou solucionar problemas de configuração local. Você pode baixar o código aqui. Certifique-se de ter o docker configurado localmente antes de executar o código. Você pode baixar o Docker aqui.
  1. Alterar diretórios para o diretório Docker.
    1cd docker
  2. Construa a imagem do Docker com um nome de tag. Dentro deste diretório, execute:
    1docker build . -t mdb-csfle-example
    Isso criará uma imagem Docker com o nome de tag mdb-csfle-example.
  3. Execute o seguinte comando para executar a imagem do Docker:
    1docker run -tih csfle mdb-csfle-example
    O comando acima executará uma imagem Docker com tag mdb-csfle-example e fornecerá csfle como seu nome de host.
  4. Quando estiver dentro do Docker container, você pode seguir as etapas abaixo para executar o exemplo de código NodeJS.
    1$ export MONGODB_URL="mongodb+srv://USER:PWD@EXAMPLE.mongodb.net/dbname?retryWrites=true&w=majority"
    2
    3$ node ./example.js
    Observação: se você estiver se conectando ao MongoDB Atlas, certifique-se de Configure Allowlist Entries.

Resumo

Queríamos desenvolver um sistema que armazenasse com segurança registros médicos confidenciais para os pacientes. Também queríamos fortes garantias de acesso e segurança a dados que não dependessem de usuários individuais. Depois de pesquisar as opções disponíveis, determinamos que a criptografia em nível de campo do lado do cliente do MongoDB atende aos requisitos e decidimos implementá-la em seu aplicativo. Para implementar o CSFLE, fizemos o seguinte:
1. Criou uma chave de encriptação mestra gerenciada localmente
Uma chave mestra gerenciada localmente nos permitiu desenvolver rapidamente o aplicativo cliente sem dependências externas e evitar o vazamento acidental de credenciais de produção confidenciais.
2. Gerou uma chave de dados criptografada com a chave mestra
O CSFLE usa criptografia de envelope, então geramos uma chave de dados que criptografa e descriptografa cada campo e, em seguida, criptografamos a chave de dados usando uma chave mestra. Isso nos permite armazenar a chave de dados criptografada no MongoDB para que ela seja compartilhada com todos os clientes e, ao mesmo tempo, impedir o acesso de clientes que não têm acesso à chave mestre.
3. Criou um JSON Schema
CSFLE pode criptografar e descriptografar campos automaticamente com base em um JSON schema fornecido que especifica quais campos criptografar e como criptografá-los.
4. Queries testadas e validadas com o cliente CSFLE
Testamos sua implementação de CSFLE inserindo e consultando documentos com campos criptografados. Em seguida, validamos que os clientes sem o CSFLE ativado não podiam ler os dados criptografados.

Mover para produção

Neste guia, armazenamos a chave mestra em seu sistema de arquivos local. Como as chaves de criptografia de dados podem ser lidas por qualquer pessoa que tenha acesso direto à chave mestra, recomendamos enfaticamente que você use um local de armazenamento mais seguro, como um sistema de gerenciamento de chaves (KMS).

Leitura adicional

Para obter mais informações sobre a criptografia no nível do campo do lado do cliente no MongoDB, confira os Docs no manual do servidor:

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Tutorial

Introdução ao MongoDB e ao AWS Codewhisperer


Sep 26, 2024 | 3 min read
Início rápido

Aggregation Framework com Node.js 3.3.2 Tutorial


Oct 01, 2024 | 9 min read
exemplo de código

Trends Analyser


Sep 11, 2024 | 1 min read
Tutorial

Dados em tempo real em um front-end do React JavaScript com Change Streams


Sep 09, 2024 | 6 min read
Sumário