Um dos principais tópicos atualmente é, sem dúvida, a segurança. Na rotina, isso pode passar despercebido, mas, cedo ou mais tarde, precisamos implementar ou seguir algumas diretrizes de segurança. Hoje, vamos discutir uma delas: a Criptografia de Campo.
Introdução.
Falando especificamente sobre o recurso, ele está disponível apenas a partir das versões 4.2+ do MongoDB. O MongoDB oferece dois métodos de Criptografia de Campos:- Criptografia automática no nível do campo do lado do cliente
- Criptografia explícita (manual) no nível do campo do lado do cliente
How to:
- 1. Para começar a usar o recurso e para fins ilustrativos, utilizaremos um Keyfile gerenciado localmente como nosso Serviço de Gerenciamento de Chaves(Key Management Service – KMS).
- Amazon Web Services KMS
- Azure Key Vault
- Google Cloud Platform KMS
1 2 3 |
openssl rand -hex 50 | head -c 96 | base64 | tr -d '\n' > /localkeys/client.key chmod 600 /localkeys/client.key chown mongod:mongod /localkeys/client.key |
- 2. Após criar o keyfile, vamos abrir uma sessão do mongo shell com o banco de dados, mas sem nos conectar ainda.
--nodb
para iniciar o shell sem conexão imediata ao banco de dados. Também utilizaremos --shell
para executar o código fornecido (neste caso, o valor da string fornecida ao --eval
) sem que a sessão seja encerrada automaticamente no final.
Esse passo é necessário porque precisamos carregar o keyfile em um objeto que posteriormente se tornará uma propriedade da conexão com o banco de dados. No exemplo a seguir, estamos carregando o keyfile na variável de banco de dados LOCAL_KEY:
1 2 3 4 5 6 7 8 |
ClientSideFieldLevelEncryptionOptions = { keyVaultNamespace: "encryption.__keyVault", // Nome do namespace do key vault kmsProviders: { local: { key: BinData(0, LOCAL_KEY) // Insere a chave local carregada anteriormente } } }; |
- 4. Após definir a variável, podemos estabelecer uma conexão habilitada para FLE (Field Level Encryption) utilizando as variáveis configuradas acima no construtor
Mongo()
.
mongodb://.../
.
1 2 |
mongo> csfleDatabaseConnection = Mongo("mongodb://dba:secret@localhost:27017/?authSource=admin", ClientSideFieldLevelEncryptionOptions) connection to localhost:27017 |
- 5. O próximo passo será criar o objeto interno do key vault em si. Até o momento, estávamos definindo variáveis e ajustando as conexões do cliente para prosseguir. Isso pode ser feito da seguinte maneira para o key vault:
1 2 3 4 5 |
mongo > keyVault = csfleDatabaseConnection.getKeyVault(); { "mongo": connection to localhost: 27017, "keyColl": encryption.__dataKeys } |
1 2 3 4 5 6 7 8 9 10 |
> show dbs admin 0.000GB config 0.000GB encryption 0.000GB > use encryption switched to db encryption > show collections __dataKeys |
- 6. Com tudo configurado e as estruturas do key vault implementadas, o próximo passo é adicionar uma chave de criptografia de dados ao key vault da conexão do banco de dados. Se bem-sucedido, o comando
createKey()
retorna o UUID da nova chave de criptografia de dados. Este UUID, que é um BSON Binary, será usado para criptografar os campos manualmente:
1 2 3 4 5 6 |
mongo> keyVault.createKey( "local", /* Chave do tipo Local */ "", /* Chave mestre do cliente, usada com KMS externos */ [ "myFirstCSFLEDataKey" ] ) UUID("5bd46d64-3fe8-4e31-a800-219eaa1b6a85") |
- 7. Agora, vamos inserir um documento utilizando a chave criada acima para criptografar.
Note que todas as operações anteriores foram feitas na mesma sessão do mongo-shell, e para criptografar, usaremos o método
encrypt()
junto com os parâmetros necessários para ocultar o SSN: “123-45-6789”. - 7.1. A função
encrypt()
requer os seguintes 3 argumentos para criptografar um campo:- encryptionKeyId – Este é o UUID da chave gerada.
- Value – Neste exemplo, ocultaremos o valor “123-45-6789” do campo SSN.
- encryptionAlgorithm – O algoritmo de criptografia a ser usado. Podemos escolher entre:
- Deterministic Encryption
- Randomized Encryption
- 7.2. Com tudo configurado, podemos inserir o documento criptografando o campo da seguinte maneira:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
mongo> clientEncryption = csfleDatabaseConnection.getClientEncryption(); mongo> var csfleDB = csfleDatabaseConnection.getDB("percona"); mongo> csfleDB.getCollection("newcollection").insert({ "_id": 1, "medRecNum": 1, "firstName": "Jose", "lastName": "Pereira", "ssn": clientEncryption.encrypt( UUID("47130fb5-987c-4af0-9e83-5eaf672d608b"), "123-45-6789", "AEAD_AES_256_CBC_HMAC_SHA_512-Random" ), "comment": "Jose Pereira's SSN encrypted." }); WriteResult({ "nInserted" : 1 }) |
“Como posso ler esse valor criptografado?”
Vamos ver como podemos fazer isso na próxima seção abaixo.
Leitura de um Campo Criptografado
Neste ponto, se nos conectarmos ao banco de dados sem passar a configuração de criptografia, não conseguiremos ler as informações.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
shell#> mongo "mongodb://dba:secret@localhost:27017/?authSource=admin" mongo> use percona switched to db percona mongo-shell-2> show collections newcollection mongo-shell-2> db.newcollection.find().pretty() { "_id" : 1, "medRecNum" : 1, "firstName" : "Jose", "lastName" : "Pereira", "ssn" : BinData(6,"AkcTD7WYfErwnoNer2ctYIsCVXS2nJYpSEgYFlp8ORmZ1i9PO/RGELdm+XxZyN6+ls+KLeDu1LQFtIIJs1Bwy5AMnaA3Lf4qAfm0Nmov6Iwuqer67HV2nIQk6dIa98QFLXs="), "comment" : "Jose Pereira's SSN encrypted." } exit |
- 1. Para ler o campo, o cliente deve carregar a configuração de
ClientSideFieldLevelEncryptionOptions
em sua sessão. Isso envolve abrir uma conexão, carregar as opções em uma variável e usar o construtorMongo()
, semelhante ao que fizemos antes, mas, desta vez, sem a configuração de key vault:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
shell># mongo --shell --nodb --eval "var LOCAL_KEY = cat('/localkeys/client.key')" Percona Server for MongoDB shell version v4.4.3-5 type "help" for help mongo> var ClientSideFieldLevelEncryptionOptions = { "keyVaultNamespace" : "encryption.__dataKeys", "kmsProviders" : { "local" : { "key" : BinData(0, LOCAL_KEY) } } } mongo> csfleDatabaseConnection = Mongo("mongodb://dba:secret@localhost:27017/?authSource=admin", ClientSideFieldLevelEncryptionOptions) connection to localhost:27017 |
- 2. Com a configuração feita, podemos executar um
find()
para retornar o documento descriptografado: