Criptografia em uso: a maciez do MongoDB para queries seguras
Avalie esse Tutorial
Você é responsável por um sistema de folha de pagamento em uma empresa de tecnologia em rápido crescimento. Os funcionários confiam em você para proteger dados altamente confidenciais – coisas como salários, Bônus e informações pessoais. Enquanto isso, regulamentações de privacidade como GDPR e LGPD exigem conformidade rigorosa, não deixando espaço para erros. As riscos são altas: proteger os dados ou correr o risco de multas e quebra de confiança.
Mas a criptografia tradicional não é suficiente. Proteger dados enquanto eles estão em trânsito ou armazenados resolve apenas parte do problema. O que acontece quando o RH precisa executar uma query nesses dados? Você precisa de uma solução de criptografia que funcione perfeitamente, mesmo durante o processamento de dados — uma que mantenha tudo seguro, mas que ainda permita pesquisas significativas, como “find all employees earning over $10,000.” É aqui que entram o CSFLE e a Queryable Encryption do MongoDB, oferecendo ferramentas de ponta para equilibrar segurança e usabilidade .
Antes de mergulhar nas especificidades do CSFLE e da Queryable Encryption, vamos revisar os principais métodos de criptografia que o MongoDB oferece:
O MongoDB protege os dados em movimento criptografando o tráfego cliente-servidor com TLS/SSL. Este método é essencial para proteger dados durante a transmissão de rede. Além disso, todas as comunicações entre nós e processos são criptografadas com TLS, garantindo segurança abrangente para dados em movimento.
A encryption at rest protege os dados armazenados em disco, disponíveis nas versões Enterprise Advanced e Atlas . Essa abordagem protege os dados mesmo se um invasor obtiver acesso ao disco ou aos arquivos do banco de dados subjacente, garantindo a confidencialidade além da segurança do servidor .
Protege dados em todas as etapas da transmissão, armazenamento e processamento do seu ciclo de vida. O MongoDB oferece duas abordagens para criptografia em uso:
- Queryable Encryption
- Criptografia no nível do campo do lado do cliente (CSFLE)
Embora haja alguma sobreposição, há diferenças importantes entre o CSFLE e o Queryable Encryption.
- Usa criptografia determinística para campos que precisam de queries de igualdade, produzindo textos cifrados idênticos para valores idênticos
- Habilita queries de correspondência exata, mas pode levar a um vazamento significativo de informações em alguns casos, pois podem surgir padrões, especialmente com dados de baixa cardinalidade
- Usa criptografia não determinística, gerando textos cifrados exclusivos para valores idênticos, dificultando os ataques
- Suporta queries de igualdade e, a partir do MongoDB,8.0 queries de faixa usando operadores como
$lt
,$lte
,$gt
e$gte
- Suporta somente queries de correspondência exata (igualdade) em campos criptografados deterministicamente.
- adequado para cenários que não exigem operações de query complexas; isto torna o CSFLE ideal onde a igualdade é o único tipo de query necessário
- Oferece suporte a query avançada para dados criptografados; além das queries de igualdade, o MongoDB 8.0 adiciona suporte para queries de faixa usando operadores como
$lt
,$lte
,$gt
e$gte
- Em breve expandirá essa funcionalidade para incluir query de prefixo, sufixo e substring, aumentando a flexibilidade para query de dados criptografados
- Ideal para cenários em que o controle total sobre as chaves de criptografia é necessário e as queries de igualdade são suficientes para os requisitos do aplicação
- Particularmente útil para dados altamente confidenciais que exigem proteção em toda a comunicação cliente-servidor, oferecendo controle granular sobre o processo de criptografia
- Recomendado para aplicativos que exigem consultas complexas de dados criptografados, como intervalos de datas ou valores numéricos
- Seu design vaza menos informações sobre os dados criptografados do que o CSFLE; trata-se de código aberto e os algoritmos são publicados e analisados por especialistas externos em criptografia que analisam a segurança de seu design e implementação
Para demonstrar a implementação, criaremos um aplicação Python simples para gerenciar com segurança as informações dos funcionários, armazenando e consultando dados confidenciais (como nomes e salários) usando a criptografia avançada do MongoDB.
A tabela a seguir mostra quais edições do MongoDB suportam CSFLE e Queryable Encryption:
Nome do produto | Suporte à criptografia automática | Suporte à criptografia explícita |
---|---|---|
MongoDB Atlas | sim | sim |
MongoDB Enterprise Advanced | sim | sim |
MongoDB Community Edition | No | sim |
- Instale o módulo Python:
1 pip install pymongo 2 python -m pip install 'pymongo[encryption]'
Para criptografar dados, usamos uma chave mestre, que pode ser carregada de um arquivo local (somente para fins de demonstração). O código a seguir verifica se o arquivo de chave existe e o cria, se necessário. Para criar um novo provedor local, crie uma chave com este comando: Para shell Unix:
1 echo $(head -c 96 /dev/urandom | base64 | tr -d '\n')
Para PowerShell:
1 $r=[byte[]]::new(64);$g=[System.Security.Cryptography.RandomNumberGenerator]::Create();$g.GetBytes($r);[Convert]::ToBase64String($r)
Observação: um fornecedor de chaves local não é seguro para ambientes de produção. Para produção, use um sistema de gerenciamento de chaves remotas (KMS), como Amazon Web Services KMS, Azure Key Vault ou Google Cloud Platform KMS para maior segurança e controle de acesso. O MongoDB também oferece suporte a provedores locais de KMIP, como o HashiCorp Vault, para gerenciamento de chaves de nível de produção. Coloque a chave no código abaixo em Your_MASTER_KEY_HERE.
1 def load_local_master_key(filename): 2 if not os.path.exists(filename): 3 key = "YOUR_MASTER_KEY_HERE" 4 with open(filename, "w") as f: 5 f.write(key) 6 with open(filename, "r") as f: 7 return f.read()
Defina as variáveis para a string de conexão, o banco de dados e os nomes de coleção do MongoDB , bem como o namespace do cofre de chaves e os fornecedores de KMS.
Observação: substitua o URI pelo URI do MongoDB Atlas .
1 uri = "<URI>" 2 key_vault_namespace = "encryption.__keyVault" 3 encrypted_database_name = "employee_data" 4 encrypted_collection_name = "employee_salary" 5 6 local_master_key_file = "local_master_key.txt" 7 local_master_key = load_local_master_key(local_master_key_file) 8 9 kms_providers = {"local": {"key": local_master_key}} 10 encrypted_fields_map = get_encrypted_fields_map()
A função getEncryptedFieldsMap especifica quais campos serão criptografados e os tipos de query permitidos. Neste exemplo, name suporta queries de igualdade e ordenador suporta queries de faixa.
1 def get_encrypted_fields_map(): 2 return { 3 "fields": [ 4 { 5 "keyId": None, 6 "path": "name", 7 "bsonType": "string", 8 "queries": [{"queryType": "equality"}], 9 }, 10 { 11 "keyId": None, 12 "path": "salary", 13 "bsonType": "int", 14 "queries": [{"queryType": "range", "min": 0, "max": 1000000}], 15 }, 16 ] 17 }
Configure as opções de criptografia automática com a função AutoEncryptionOpts, especificando o namespace do cofre de chaves, o provedor KMS e o caminho da biblioteca de criptografia compartilhada.
Observação: na função AutoEncryptionOpts, substitua o caminho para a biblioteca de criptografia compartilhada pelo caminho correto no seu sistema.
1 auto_encryption_opts = AutoEncryptionOpts( 2 kms_providers=kms_providers, 3 key_vault_namespace=key_vault_namespace, 4 crypt_shared_lib_path="./mongo_crypt_shared_v1-macos-arm64-enterprise-8.0.3/lib/mongo_crypt_v1.dylib", 5 encrypted_fields_map=encrypted_fields_map, 6 )
Crie um cliente MongoDB com a criptografia automática habilitada.
1 client = MongoClient(uri, auto_encryption_opts=auto_encryption_opts) 2 db = client[encrypted_database_name] 3 collection = db[encrypted_collection_name] 4 collection.drop()
Insira documentos de exemplo na coleção criptografada, com informações confidenciais protegidas por criptografia.
1 employees = [ 2 EmployeeDocument("Alice Johnson", "Software Engineer", "MongoDB", 100000, "USD", datetime.datetime(2019, 3, 5)), 3 EmployeeDocument("Bob Smith", "Product Manager", "MongoDB", 150000, "USD", datetime.datetime(2018, 6, 15)), 4 EmployeeDocument("Charlie Brown", "Data Analyst", "MongoDB", 200000, "USD", datetime.datetime(2020, 4, 20)), 5 EmployeeDocument("Diana Prince", "HR Specialist", "MongoDB", 250000, "USD", datetime.datetime(2021, 12, 3)), 6 EmployeeDocument("Evan Peters", "Marketing Coordinator", "MongoDB", 80000, "USD", datetime.datetime(2022, 10, 7)), 7 ] 8 9 for employee in employees: 10 collection.insert_one(employee.__dict__) 11 print(f"Inserted document for {employee.name}")
Sem a chave, não podemos ver os dados. Funcionou.
Finalmente, execute queries na coleção criptografada. A função search_by_name pesquisa documentos por nome, enquanto search_by_salary_range usa um filtro de faixa de salário.
1 def search_by_name(coll, name): 2 filter = {"name": name} 3 cursor = coll.find(filter) 4 print(f"Documents with name '{name}':") 5 for doc in cursor: 6 print(json.dumps(doc, indent=4, default=json_util.default)) 7 8 def search_by_salary_range(coll, min_salary, max_salary): 9 filter = {"salary": {"$gte": min_salary, "$lte": max_salary}} 10 cursor = coll.find(filter) 11 print(f"Documents with salary between {min_salary} and {max_salary}:") 12 for doc in cursor: 13 print(json.dumps(doc, indent=4, default=json_util.default)) 14 15 search_by_name(collection, "Alice Johnson") 16 search_by_salary_range(collection, 150000, 200000)
Como podemos ver, na versão do MongoDB 8, também podemos fazer isso com intervalos de dados. Você pode conferir o código inteiro no repositório do Github e mais informações na documentação do MongoDB .
Defina as variáveis para a string de conexão, o banco de dados e os nomes de coleção do MongoDB , bem como o namespace do cofre de chaves e os fornecedores de KMS. Observação: substitua o URI pelo URI do MongoDB Atlas .
1 uri = "<URI>" 2 local_master_key_file = "local_master_key.txt" 3 local_master_key = load_local_master_key(local_master_key_file) 4 kms_providers = setup_kms_providers(local_master_key) 5 key_vault_namespace = "encryption.__keyVault" 6 7 client = connect_mongo(uri) 8 database_name = "employee_data" 9 collection_name = "employee_salary" 10 coll = client[database_name][collection_name] 11 coll.drop() 12 key_vault_coll = client["encryption"]["__keyVault"] 13 ensure_key_vault_index(key_vault_coll)
Garantimos que o índice keyAltNames existe no Key Vault, permitindo que chaves alternativas sejam exclusivas e filtradas.
1 def ensure_key_vault_index(key_vault_coll): 2 index_name = "keyAltNames_1" 3 indexes = key_vault_coll.index_information() 4 if index_name not in indexes: 5 key_vault_coll.create_index( 6 [("keyAltNames", 1)], unique=True, partialFilterExpression={"keyAltNames": {"$exists": True}} 7 ) 8 print("Index created in key vault.") 9 else: 10 print("Index already exists in key vault.")
A função ensure_data_key verifica se já existe uma chave de dados específica. Caso contrário, é criado um novo.
1 def ensure_data_key(client_encryption, key_vault_coll, key_alt_name): 2 existing_key = key_vault_coll.find_one({"keyAltNames": key_alt_name}) 3 if existing_key: 4 print("Data key already exists. Reusing existing key.") 5 return existing_key["_id"] 6 7 print("Creating new data key.") 8 data_key_id = client_encryption.create_data_key("local", key_alt_names=[key_alt_name]) 9 return data_key_id
A função encrypt_salary converte o salário em centavos, prepara o valor para criptografia e o criptografa usando o algoritmo de criptografia configurado do MongoDB .
1 def encrypt_salary(client_encryption, data_key_id, salary): 2 salary_in_cents = int(salary * 100) 3 encrypted_salary = client_encryption.encrypt( 4 value=salary_in_cents, 5 algorithm="AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", 6 key_id=data_key_id 7 ) 8 return encrypted_salary
Depois de criptografar o salário, inserimos o documento de cada funcionário na coleção. O documento inclui campos como Nome, Cargo, Empresa, Moeda, Data de início e o salário criptografado.
1 def insert_employee_doc(coll, name, position, company, salary_encrypted, currency, start_date): 2 employee_doc = { 3 "name": name, 4 "position": position, 5 "company": company, 6 "salary": salary_encrypted, 7 "currency": currency, 8 "startDate": start_date 9 } 10 coll.insert_one(employee_doc)
1 employees = [ 2 {"name": "Alice Johnson", "position": "Software Engineer", "company": "MongoDB", "salary": 50000, "currency": "USD", "start_date": datetime(2007, 2, 3)}, 3 {"name": "Bob Smith", "position": "Product Manager", "company": "MongoDB", "salary": 70000, "currency": "USD", "start_date": datetime(2009, 3, 14)}, 4 ] 5 for emp in employees: 6 encrypted_salary = encrypt_salary(client_encryption, data_key_id, emp["salary"]) 7 insert_employee_doc(coll, emp["name"], emp["position"], emp["company"], encrypted_salary, emp["currency"], emp["start_date"])
Sem a chave, não podemos ver os dados. Funcionou.
Para consultar e visualizar o campo de salário criptografado, usamos find_all_and_decrypt_salaries, que recupera todos os documentos, descriptografa o salário e exibe os dados.
1 def find_all_and_decrypt_salaries(coll, client_encryption): 2 for doc in coll.find(): 3 encrypted_salary = doc["salary"] 4 decrypted_salary = client_encryption.decrypt(encrypted_salary) 5 salary_in_dollars = decrypted_salary / 100.0 6 print(f"Employee: {doc['name']}, Salary: {salary_in_dollars} USD")
Para comparação, a função find_all_without_decryption recupera os mesmos documentos, mas sem descriptografar os salários.
1 def find_all_without_decryption(coll): 2 for doc in coll.find(): 3 print(f"Employee: {doc['name']}, Encrypted Salary: {doc['salary']}")
O resultado é:
Vamos aumentar o zoom e ver a diferença entre dois objetos, um criptografado e outro não.
Como podemos ver nesta imagem, o valor real do objeto usado sem criptografia vem em um formato ilegível para nós sem a chave de criptografia. Você pode conferir o código inteiro no repositório do Github e mais informações na documentação do MongoDB .
CSFLE e Queryable Encryption são soluções avançadas de criptografia no MongoDB, que fornecem métodos distintos para proteger dados confidenciais e habilitar queries seguras. O CSFLE é ideal para casos em que as queries de controle e igualdade do lado do cliente são suficientes, enquanto a Queryable Encryption é eficaz para cenários que exigem queries de intervalo, com suporte futuro para tipos de query mais avançados, como pesquisas de substring, em desenvolvimento. Com o suporte a query de intervalo do MongoDB 8.0, a Queryable Encryption se torna ainda mais poderosa e flexível para proteger dados confidenciais.
Usando o Python, você pode configurar e usar facilmente essas soluções de criptografia para aumentar a segurança dos aplicação MongoDB , atendendo aos rigorosos requisitos de conformidade e segurança de dados.
Para obter mais recursos e ferramentas do MongoDB , visite o Centro do MongoDB Developer para explorar artigos adicionais.
Principais comentários nos fóruns
Ainda não há comentários sobre este artigo.