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 .

Saiba por que o MongoDB foi selecionado como um líder no 2024 Gartner_Magic Quadrupnt()
Desenvolvedor do MongoDB
Central de desenvolvedor do MongoDBchevron-right
Idiomaschevron-right
Gochevron-right

Criptografia no nível do campo do lado do cliente (CSFLE) no MongoDB com Golang

Nic Raboy15 min read • Published Jan 14, 2022 • Updated Feb 03, 2023
MongoDBSegurançaGo
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Uma das muitas coisas boas sobre o MongoDB é o quão seguro você pode tornar seus dados nele. Além das regras baseadas em rede e usuário, você tem criptografia de seus dados em repouso, criptografia pela conexão e, agora, recentemente, criptografia do lado do cliente conhecida como criptografia de nível de campo do lado do cliente (CSFLE).
Então, o que é exatamente a criptografia no nível do campo do lado do cliente (CSFLE) e como você a usa?
Com a criptografia em nível de campo, você pode optar por criptografar determinados campos em um documento, do lado do cliente, enquanto deixa outros campos como texto sem formatação. Isso é particularmente útil porque, ao visualizar um documento CSFLE com a CLI, oCompassou diretamente no Altas, os campos criptografados não serão legíveis por humanos. Quando não forem legíveis por humanos, se os documentos cairem em mãos erradas, esses campos serão inúteis para o usuário mal-intencionado. No entanto, ao usar os drivers de linguagem MongoDB e as mesmas chaves de criptografia, esses campos podem ser descriptografados e consultados no aplicativo.
Neste tutorial de início rápido, vamos ver como usar a criptografia em nível de campo do MongoDB com a linguagem de programação Go (Golang). Em particular, vamos explorar a criptografia automática em vez da criptografia manual.

Os requisitos

Existem alguns requisitos que devem ser atendidos antes de tentar usar o CSFLE com o driver Go.
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 Go que usam MongoDB. Se quiser se atualizar, dê uma olhada na série de início rápido que publiquei sobre o tópico.
Para usar a criptografia em nível de campo, você precisará de um pouco mais do que apenas ter uma versão apropriada do MongoDB e do driver MongoDB Go. Vamos precisar delibmongocrypt, que é uma biblioteca complementar para criptografia nos drivers do MongoDB, e mongocryptd, que é um binário para analisar regras de criptografia automática com base no formato JSON estendido.

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.
Existem algumas soluções para instalar a bibliotecalibmongocrypt no macOS, sendo a mais fácil com o Homebrew. Se você tiver o Homebrew instalado, poderá instalar alibmongocrypt com o seguinte comando:
1brew install mongodb/brew/libmongocrypt
Só assim, o driver MongoDB Go será capaz de lidar com a criptografia. Mais explicações sobre as instruções podem ser encontradas na documentação.
Como queremos fazer a criptografia automática com o driver usando um JSON schema estendido, precisamos demongocryptd, um binário fornecido com o MongoDB Enterprise Edition. O bináriomongocryptd precisa existir no computador ou servidor onde o aplicativo Go pretende ser executado. Não é uma dependência de desenvolvimento como libmongocrypt, mas uma dependência de tempo de execução.
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 .
A essa altura, todos os componentes apropriados para a criptografia em nível de campo devem estar instalados ou disponíveis.

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

Antes de começarmos a criptografar e descriptografar campos em nossos documentos, precisamos estabelecer chaves para fazer a maior parte do trabalho. Isso significa definir a localização do nosso cofre de chaves no MongoDB e no Sistema de Gerenciamento de Chaves (KMS) que desejamos usar para descriptografar as chaves de criptografia de dados.
O cofre de chaves é uma collection que criaremos no MongoDB para armazenar chaves criptografadas para os campos de nossos documentos. A chave primária dentro do KMS descriptografará as chaves dentro do cofre de chaves.
Para este tutorial específico, vamos usar um provedor de chaves locais para o nosso KMS. vale a pena procurar algo como AWS KMS ou semelhante, algo que exploraremos em um tutorial futuro, como alternativa a um provedor de chave local.
No seu computador, crie um novo projeto Go com o seguinte arquivo principal.go:
1package main
2
3import (
4 "context"
5 "crypto/rand"
6 "fmt"
7 "io/ioutil"
8 "log"
9 "os"
10
11 "go.mongodb.org/mongo-driver/bson"
12 "go.mongodb.org/mongo-driver/mongo"
13 "go.mongodb.org/mongo-driver/mongo/options"
14)
15
16var (
17 ctx = context.Background()
18 kmsProviders map[string]map[string]interface{}
19 schemaMap bson.M
20)
21
22func createDataKey() {}
23func createEncryptedClient() *mongo.Client {}
24func readSchemaFromFile(file string) bson.M {}
25
26func main() {}
Você precisará instalar o driver MongoDB Go para continuar. Para saber como fazer isso, reserve um momento para conferir meu tutorial anterior intitulado Quick Start: Golang & MongoDB - Starting and Setup.
No código acima, temos algumas variáveis definidas, bem como algumas funções. Vamos nos concentrar na variável kmsProviderse na funçãocreateDataKey para esta parte específica do tutorial.
Dê uma olhada na seguinte funçãocreateDataKey:
1func createDataKey() {
2 kvClient, err := mongo.Connect(ctx, options.Client().ApplyURI(os.Getenv("ATLAS_URI")))
3 if err != nil {
4 log.Fatal(err)
5 }
6 clientEncryptionOpts := options.ClientEncryption().SetKeyVaultNamespace("keyvault.datakeys").SetKmsProviders(kmsProviders)
7 clientEncryption, err := mongo.NewClientEncryption(kvClient, clientEncryptionOpts)
8 if err != nil {
9 log.Fatal(err)
10 }
11 defer clientEncryption.Close(ctx)
12 _, err = clientEncryption.CreateDataKey(ctx, "local", options.DataKey().SetKeyAltNames([]string{"example"}))
13 if err != nil {
14 log.Fatal(err)
15 }
16}
Na função createDataKeyacima, estamos nos conectando pela primeira vez ao MongoDB. A connection string do MongoDB é definida pela variável de ambiente ATLAS_URI no código acima. Embora você possa codificar essa connection string ou armazená-la em um arquivo de configuração, por motivos de segurança, faz muito sentido usar variáveis de ambiente.
Se a conexão for bem-sucedida, precisaremos definir o espaço de nome do cofre de chaves e o provedor KMS como parte das opções de configuração de criptografia. O namespace é composto pelo nome do banco de dados seguido pelo nome da collection. É aqui que as informações da chave serão armazenadas. O mapakmsProviders, que será definido posteriormente, terá informações de chaves locais.
A execução da funçãoCreateDataKeycriará as informações-chave no MongoDB como um documento.
Optamos por especificar um nome de chave alternativo de example para que não precisemos nos referir à chave de dados pelo _id ao usá-la em nossos documentos. Em vez disso, poderemos usar o nome alternativo exclusivo que pode seguir uma convenção de nomenclatura especial. É importante observar que o nome alternativo da chave só é útil quando se usa o AEAD_AES_256_CBC_HMAC_SHA_512-Random, algo que exploraremos mais adiante neste tutorial.
Para usar a função createDataKey, podemos fazer algumas modificações na funçãomain:
1func main() {
2 localKey := make([]byte, 96)
3 if _, err := rand.Read(localKey); err != nil {
4 log.Fatal(err)
5 }
6 kmsProviders = map[string]map[string]interface{}{
7 "local": {
8 "key": localKey,
9 },
10 }
11 createDataKey()
12}
No código acima, estamos gerando uma chave aleatória. Esta chave aleatória é adicionada ao mapakmsProviders que estamos usando dentro da funçãocreateDataKey.
Não é seguro ter sua chave local armazenada dentro do aplicativo ou no mesmo servidor. Na produção, considere usar o AWS KMS ou acessar sua chave local por meio de uma solicitação separada antes de adicioná-la ao provedor de chave local.
Se você executou o código até agora, acabaria com um banco de dadoskeyvault e uma coleçãodatakeys que possui um documento de uma chave com um nome alternativo. Esse documento pareceria algo assim:
1{
2 "_id": UUID("27a51d69-809f-4cb9-ae15-d63f7eab1585"),
3 "keyAltNames": [
4 "example"
5 ],
6 "keyMaterial": Binary("oJ6lEzjIEskHFxz7zXqddCgl64EcP1A7E/r9zT+OL19/ZXVwDnEjGYMvx+BgcnzJZqkXTFTgJeaRYO/fWk5bEcYkuvXhKqpMq2ZO", 0),
7 "creationDate": 2020-11-05T23:32:26.466+00:00,
8 "updateDate": 2020-11-05T23:32:26.466+00:00,
9 "status": 0,
10 "masterKey": {
11 "provider": "local"
12 }
13}
Há algumas coisas importantes a serem observadas em nosso código até agora:
  • O localKey é aleatório e não persiste além do tempo de execução, o que resultará em incompatibilidades de chave em execuções consecutivas do aplicativo. Especifique uma chave não aleatória ou armazene-a em algum lugar após a geração.
  • Estamos usando um provedor de chaves locais com uma chave que existe localmente. Isso não é recomendado em um cenário de produção devido a preocupações de segurança. Em vez disso, use um fornecedor como Amazon Web Services KMS ou armazene a chave externamente.
  • O createDataKey só deve ser executado quando uma chave específica precisar ser criada, não sempre que o aplicativo for executado.
  • Não há nenhuma convenção de nomenclatura estrita para o cofre de chaves e as chaves que residem nele. Dê um nome ao seu banco de dados e à collection da forma que fizer sentido para você.
Depois de executarmos nosso aplicativo pela primeira vez, provavelmente vamos querer comentar a linha createDataKey na função main.

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.
Dê uma olhada no seguinte mapa de esquema para criptografia:
1{
2 "fle-example.people": {
3 "encryptMetadata": {
4 "keyId": "/keyAltName"
5 },
6 "properties": {
7 "ssn": {
8 "encrypt": {
9 "bsonType": "string",
10 "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
11 }
12 }
13 },
14 "bsonType": "object"
15 }
16}
Vamos presumir que o JSON acima exista em um arquivoschema.json que seja relativo aos nossos arquivos Go ou binário. No JSON acima, estamos dizendo que o mapa se aplica à coleçãopeople dentro do banco de dadosfle-example.
O campo keyIddentro do objeto encryptMetadatadiz que os documentos dentro da coleçãopeople devem ter um campo de string chamado keyAltName. O valor desse campo refletirá o nome da chave alternativa que definimos ao criar a chave de dados. Observe o / que prefixa o valor. Isso não é um erro. É um requisito para esse valor específico, pois se trata de um ponteiro.
O campo propertieslista os campos do nosso documento e, neste exemplo, lista os campos que devem ser criptografados juntamente com o algoritmo de criptografia a ser usado. Em nosso exemplo, somente o campossn será criptografado, enquanto todos os outros campos permanecerão como texto simples.
Existem dois algoritmos atualmente suportados:
  • AEAD_AES_256_CBC_HMAC_SHA_512-Random
  • AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic
Em suma, o algoritmo AEAD_AES_256_CBC_HMAC_SHA_512-Randomé melhor usado em campos com baixa cardinalidade ou que não precisam ser usados em um filtro para uma consulta. O algoritmoAEAD_AES_256_CBC_HMAC_SHA_512-Deterministic deve ser usado para campos com alta cardinalidade ou para campos que precisam ser usados em um filtro.
Para saber mais sobre esses algoritmos, acesse a documentação. Exploraremos ambos os algoritmos neste tutorial específico.
Se quisermos, poderíamos alterar o mapa de esquema para o seguinte:
1{
2 "fle-example.people": {
3 "properties": {
4 "ssn": {
5 "encrypt": {
6 "keyId": "/keyAltName",
7 "bsonType": "string",
8 "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
9 }
10 }
11 },
12 "bsonType": "object"
13 }
14}
A alteração feita no exemplo acima tem a ver com o campokeyId. Em vez de declará-lo como parte do encryptMetadata, nós o declaramos como parte de um campo específico. Isso pode ser útil se você quiser usar chaves diferentes para campos diferentes.
Lembre-se de que o ponteiro usado para o keyId só funcionará com o algoritmoAEAD_AES_256_CBC_HMAC_SHA_512-Random . No entanto, você pode usar o ID da chave real para ambos os algoritmos.
Com um mapa de esquema para criptografia disponível, vamos carregá-lo no aplicativo Go. Altere a funçãoreadSchemaFromFile para ter a seguinte aparência:
1func readSchemaFromFile(file string) bson.M {
2 content, err := ioutil.ReadFile(file)
3 if err != nil {
4 log.Fatal(err)
5 }
6 var doc bson.M
7 if err = bson.UnmarshalExtJSON(content, false, &doc); err != nil {
8 log.Fatal(err)
9 }
10 return doc
11}
No código acima, estamos lendo o arquivo, que será o arquivoschema.json em breve. Se for lido com sucesso, usamos a funçãoUnmarshalExtJSON para carregá-lo em um objetobson.M que seja mais fácil de trabalhar no Go.

Habilitando a criptografia automática de cliente do MongoDB em um aplicativo Golang

Nesse ponto, você deve ter o código pronto para criar uma chave de dados e um mapa de esquema definidos para serem usados com a funcionalidade de criptografia automática de cliente que o MongoDB suporta. É hora de reuni-los para realmente criptografar e descriptografar campos.
Vamos começar com a funçãocreateEncryptedClientem nosso projeto:
1func createEncryptedClient() *mongo.Client {
2 schemaMap = readSchemaFromFile("schema.json")
3 mongocryptdOpts := map[string]interface{}{
4 "mongodcryptdBypassSpawn": true,
5 }
6 autoEncryptionOpts := options.AutoEncryption().
7 SetKeyVaultNamespace("keyvault.datakeys").
8 SetKmsProviders(kmsProviders).
9 SetSchemaMap(schemaMap).
10 SetExtraOptions(mongocryptdOpts)
11 mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(os.Getenv("ATLAS_URI")).SetAutoEncryptionOptions(autoEncryptionOpts))
12 if err != nil {
13 log.Fatal(err)
14 }
15 return mongoClient
16}
No código acima, estamos usando a funçãoreadSchemaFromFile que acabamos de criar para carregar nosso mapa de esquema para criptografia. Em seguida, estamos definindo nossas opções de criptografia automática e estabelecendo uma conexão com o MongoDB. Isso parecerá um tanto familiar com o que fizemos na função createDataKey. Ao definir as opções de criptografia automática, não apenas especificamos o KMS para nossa chave e cofre, mas também fornecemos o mapa de esquema para criptografia.
Você notará que estamos usando mongocryptdBypassSpawn como uma opção extra. Estamos fazendo isso para que o cliente não tente iniciar automaticamente o daemonmongocryptd se ele já estiver em execução. Você pode ou não querer usar isso em seu próprio aplicativo.
Se a conexão for bem-sucedida, o cliente é retornado.
É hora de revisitar a funçãomain dentro do projeto:
1func main() {
2 localKey := make([]byte, 96)
3 if _, err := rand.Read(localKey); err != nil {
4 log.Fatal(err)
5 }
6 kmsProviders = map[string]map[string]interface{}{
7 "local": {
8 "key": localKey,
9 },
10 }
11 // createDataKey()
12 client := createEncryptedClient()
13 defer client.Disconnect(ctx)
14 collection := client.Database("fle-example").Collection("people")
15 if _, err := collection.InsertOne(context.TODO(), bson.M{"name": "Nic Raboy", "ssn": "123456", "keyAltName": "example"}); err != nil {
16 log.Fatal(err)
17 }
18 result, err := collection.FindOne(context.TODO(), bson.D{}).DecodeBytes()
19 if err != nil {
20 log.Fatal(err)
21 }
22 fmt.Println(result)
23}
No código acima, estamos criando nosso provedor de chave local usando uma chave local que foi gerada aleatoriamente. Lembre-se de que essa chave deve corresponder ao que foi usado ao criar a chave de dados, portanto, aleatório pode não ser o melhor a longo prazo. Da mesma forma, uma chave local não deve ser usada na produção por motivos de segurança.
Depois que os provedores de KMS são estabelecidos, a funçãocreateEncryptedClienté executada. Lembre-se de que essa função específica definirá as opções de criptografia automática e estabelecerá uma conexão com o MongoDB.
Para corresponder ao banco de dados e à coleção usados na definição do mapa de esquema, estamos usando fle-example como banco de dados e people como coleção. As operações a seguir, como InsertOne e FindOne, podem ser usadas como se a criptografia em nível de campo não existisse. Como temos um campossne o campokeyAltName, o campossnserá criptografado no lado do cliente e salvo no MongoDB. Ao fazer a operação de pesquisa, o campo criptografado será descriptografado.
Dados FLE no MongoDB Atlas
Dados FLE no MongoDB Atlas
Ao examinar os dados no Atlas, por exemplo, os campos criptografados não serão legíveis por humanos, como visto na captura de tela acima.

Execução e criação de um aplicativo Golang com criptografia no nível do campo do MongoDB

Quando a criptografia em nível de campo é incluída no aplicativo Go, uma marcação especial deve ser incluída no processo de compilação ou execução, dependendo da rota escolhida. Você já deve ter mongocryptd e libmongocrypt, então, para construir seu aplicativo Go, você faria o seguinte:
1go build -tags cse
Se você usar o comando acima para criar seu binário, poderá usá-lo normalmente. No entanto, se você estiver executando o aplicativo sem criar, poderá fazer algo como o seguinte:
1go run -tags cse main.go
O comando acima executará o aplicativo com a criptografia do lado do cliente ativada.

Filtrar documentos no MongoDB em um campo criptografado

Se você executou o exemplo até agora, provavelmente notará que, embora possa criptografar e descriptografar campos automaticamente, receberá um erro se tentar usar um filtro que contenha um campo criptografado.
No nosso exemplo até agora, usamos o algoritmoAEAD_AES_256_CBC_HMAC_SHA_512-Random em nossos campos criptografados. Para poder filtrar campos criptografados, o AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic deve ser usado. Mais informações entre as duas opções podem ser encontradas na documentação.
Para usar a abordagem determinística, precisamos fazer algumas revisões em nosso projeto. Essas alterações são resultado do fato de que não poderemos usar nomes de chave alternativos em nosso mapa de esquema.
Primeiro, vamos alterar o arquivoschema.json para o seguinte:
1{
2 "fle-example.people": {
3 "encryptMetadata": {
4 "keyId": [
5 {
6 "$binary": {
7 "base64": "%s",
8 "subType": "04"
9 }
10 }
11 ]
12 },
13 "properties": {
14 "ssn": {
15 "encrypt": {
16 "bsonType": "string",
17 "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
18 }
19 }
20 },
21 "bsonType": "object"
22 }
23}
As duas alterações no JSON acima refletem o novo algoritmo e o keyId usando o valor_idreal em vez de um alias. Para o campobase64, observe o uso do espaço reservado%s . Se você conhece a versão de string64 básica da sua chave, substitua-a e poupe trabalho. Como este tutorial é um exemplo e os dados mudam praticamente toda vez que os executamos, provavelmente queremos trocar esse campo depois que o arquivo for carregado.
Começando com a função createDataKey, localize a seguinte linha com a chamada da funçãoCreateDataKey:
1dataKeyId, err := clientEncryption.CreateDataKey(ctx, "local", options.DataKey())
O que não vimos nas partes anteriores deste tutorial é que essa função retorna o _id da chave de dados. Provavelmente devemos atualizar nossa funçãocreateDataKeypara retornar primitive.Binary e, em seguida, retornar essa variáveldataKeyId.
Precisamos mover esse valordataKeyId até que ele chegue ao local onde carregamos nosso arquivo JSON. Estamos trabalhando muito pelos seguintes motivos:
  • Estamos em um cenário em que não sabemos o _id de nossa chave de dados antes do tempo de execução. Se o conhecermos, podemos adicioná-lo ao esquema e pronto.
  • Criamos nosso código para pular funções.
O mapa de esquema requer um valor base64 para ser usado, portanto, quando passamos dataKeyId, precisamos primeiro codificá-lo.
Na função main, podemos ter algo parecido com isto:
1dataKeyId := createDataKey()
2client := createEncryptedClient(base64.StdEncoding.EncodeToString(dataKeyId.Data))
Isso significa que o createEncryptedClient precisa receber um argumento de string. Atualize o createEncryptedClient para aceitar uma string e então alterar como estamos lendo nosso arquivo JSON:
1schemaMap = readSchemaFromFile("schema.json", dataKeyIdBase64)
Lembre-se de que estamos apenas passando o valor codificado de base64 pelo pipeline. Ao final disso, na função readSchemaFromFile, podemos atualizar nosso código para ficar assim:
1func readSchemaFromFile(file string, dataKeyIdBase64 string) bson.M {
2 content, err := ioutil.ReadFile(file)
3 if err != nil {
4 log.Fatal(err)
5 }
6 content = []byte(fmt.Sprintf(string(content), dataKeyIdBase64))
7 var doc bson.M
8 if err = bson.UnmarshalExtJSON(content, false, &doc); err != nil {
9 log.Fatal(err)
10 }
11 return doc
12}
Não apenas estamos recebendo a string64 base, mas também usando uma funçãoSprintf para trocar nosso espaço reservado%s pelo valor real.
Novamente, essas alterações foram baseadas em como projetamos nosso código. No final das contas, só estavamos mudando o keyId no mapa de esquema e o algoritmo usado para criptografia. Ao fazer isso, não só podemos descriptografar campos que foram criptografados, mas também filtrar documentos usando campos criptografados.

O código da criptografia no nível do campo (FLE) no Go

Embora possa parecer que escrevemos muito código, a realidade é que o código era muito mais simples do que os conceitos envolvidos. Para ver melhor o código, você pode encontrá-lo abaixo:
1package main
2
3import (
4 "context"
5 "crypto/rand"
6 "encoding/base64"
7 "fmt"
8 "io/ioutil"
9 "log"
10 "os"
11
12 "go.mongodb.org/mongo-driver/bson"
13 "go.mongodb.org/mongo-driver/bson/primitive"
14 "go.mongodb.org/mongo-driver/mongo"
15 "go.mongodb.org/mongo-driver/mongo/options"
16)
17
18var (
19 ctx = context.Background()
20 kmsProviders map[string]map[string]interface{}
21 schemaMap bson.M
22)
23
24func createDataKey() primitive.Binary {
25 kvClient, err := mongo.Connect(ctx, options.Client().ApplyURI(os.Getenv("ATLAS_URI")))
26 if err != nil {
27 log.Fatal(err)
28 }
29 kvClient.Database("keyvault").Collection("datakeys").Drop(ctx)
30 clientEncryptionOpts := options.ClientEncryption().SetKeyVaultNamespace("keyvault.datakeys").SetKmsProviders(kmsProviders)
31 clientEncryption, err := mongo.NewClientEncryption(kvClient, clientEncryptionOpts)
32 if err != nil {
33 log.Fatal(err)
34 }
35 defer clientEncryption.Close(ctx)
36 dataKeyId, err := clientEncryption.CreateDataKey(ctx, "local", options.DataKey())
37 if err != nil {
38 log.Fatal(err)
39 }
40 return dataKeyId
41}
42
43func createEncryptedClient(dataKeyIdBase64 string) *mongo.Client {
44 schemaMap = readSchemaFromFile("schema.json", dataKeyIdBase64)
45 mongocryptdOpts := map[string]interface{}{
46 "mongodcryptdBypassSpawn": true,
47 }
48 autoEncryptionOpts := options.AutoEncryption().
49 SetKeyVaultNamespace("keyvault.datakeys").
50 SetKmsProviders(kmsProviders).
51 SetSchemaMap(schemaMap).
52 SetExtraOptions(mongocryptdOpts)
53 mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(os.Getenv("ATLAS_URI")).SetAutoEncryptionOptions(autoEncryptionOpts))
54 if err != nil {
55 log.Fatal(err)
56 }
57 return mongoClient
58}
59
60func readSchemaFromFile(file string, dataKeyIdBase64 string) bson.M {
61 content, err := ioutil.ReadFile(file)
62 if err != nil {
63 log.Fatal(err)
64 }
65 content = []byte(fmt.Sprintf(string(content), dataKeyIdBase64))
66 var doc bson.M
67 if err = bson.UnmarshalExtJSON(content, false, &doc); err != nil {
68 log.Fatal(err)
69 }
70 return doc
71}
72
73func main() {
74 fmt.Println("Starting the application...")
75 localKey := make([]byte, 96)
76 if _, err := rand.Read(localKey); err != nil {
77 log.Fatal(err)
78 }
79 kmsProviders = map[string]map[string]interface{}{
80 "local": {
81 "key": localKey,
82 },
83 }
84 dataKeyId := createDataKey()
85 client := createEncryptedClient(base64.StdEncoding.EncodeToString(dataKeyId.Data))
86 defer client.Disconnect(ctx)
87 collection := client.Database("fle-example").Collection("people")
88 collection.Drop(context.TODO())
89 if _, err := collection.InsertOne(context.TODO(), bson.M{"name": "Nic Raboy", "ssn": "123456"}); err != nil {
90 log.Fatal(err)
91 }
92 result, err := collection.FindOne(context.TODO(), bson.M{"ssn": "123456"}).DecodeBytes()
93 if err != nil {
94 log.Fatal(err)
95 }
96 fmt.Println(result)
97}
Tente definir ATLAS_URI em suas variáveis de ambiente e dê uma olhada no código.

Solução de problemas comuns do CSFLE do MongoDB

Se você executou o código acima e encontrou alguns dados criptografados em seu banco de dados, fantástico! No entanto, se você não teve tanta sorte, quero abordar alguns dos problemas comuns que surgem.
Vamos começar com o seguinte erro de tempo de execução:
1panic: client-side encryption not enabled. add the cse build tag to support
Se você ver o erro acima, é provável que tenha esquecido de usar o sinalizador-tags cseao criar ou executar seu aplicativo. Para ir além disso, basta criar seu aplicativo com o seguinte:
1go build -tags cse
Supondo que não haja outros problemas, você não receberá mais esse erro.
Ao construir ou executar com a bandeira-tags cse, você pode encontrar o seguinte erro:
1/usr/local/Cellar/go/1.13.1/libexec/pkg/tool/darwin_amd64/link: running clang failed: exit status 1
2ld: warning: directory not found for option '-L/usr/local/Cellar/libmongocrypt/1.0.4/lib'
3ld: library not found for -lmongocrypt
4clang: error: linker command failed with exit code 1 (use -v to see invocation)
O erro pode não parecer exatamente igual ao meu, dependendo do sistema operacional que você está usando, mas a questão principal é que ele diz que está faltando a bibliotecalibmongocrypt. Certifique-se de instalá-lo corretamente para seu sistema operacional, de acordo com a documentação.
```Agora, e se você encontrar o seguinte?```
1exec: "mongocryptd": executable file not found in $PATH
2exit status 1
Como no errolibmongocrypt, isso significa apenas que não temos acesso a mongocryptd, um requisito para a criptografia automática em nível de campo. Existem vários métodos para instalar esse binário, como visto na documentação, mas no macOS isso significa ter o MongoDB Enterprise Edition por perto.

Conclusão

Você acabou de ver como usar a criptografia no nível do campo do lado do cliente (CSFLE) do MongoDB em seu aplicativo Go. Isso é útil se você quiser criptografar campos dentro de documentos do MongoDB no lado do cliente antes que eles cheguem ao banco de dados.
Para dar crédito onde o crédito é devido, grande parte do código deste tutorial foi retirado do repositório de sandboxde Kern White no Github.
Há algumas coisas que gostaria de reiterar:
  • Usar uma chave local é um risco de segurança na produção. Use algo como o AWS KMS ou carregue seu provedor de chaves locais com uma chave que foi obtida por meio de uma solicitação externa.
  • O bináriomongocryptd deve estar disponível no computador ou servidor que estiver executando o aplicativo Go. Ele é facilmente instalado por meio da instalação do MongoDB Enterprise Edition.
  • A bibliotecalibmongocrypt deve estar disponível para adicionar compatibilidade ao driver Go para criptografia e descriptografia no lado do cliente.
  • Não perde sua chave do lado do cliente. Caso contrário, você perde a capacidade de descriptografar seus campos.
Em um tutorial futuro, exploraremos como usar Amazon Web Services KMS e semelhantes para gerenciamento de chaves.
Questões? Comentários? Queremos muito nos conectar com você. Participe da conversa nos fóruns da MongoDB Community.

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

Simultaneidade e fechamento gracioso do cliente MDB


Sep 04, 2024 | 5 min read
Início rápido

Reagindo às mudanças do banco de dados com o MongoDB Change Streams e Go


Feb 03, 2023 | 5 min read
Tutorial

Desenvolvendo skills da Alexa com MongoDB e Golang


Apr 03, 2024 | 10 min read
Tutorial

Servidores HTTP persistindo dados no MongoDB


Sep 04, 2024 | 5 min read
Sumário