Sanitizar o banco do Magento 2 para staging e ambiente local
Desenvolvedores precisam de dados realistas. Uma loja de staging com cinco produtos de teste não mostra a página de categoria lenta, o produto configurável com trezentos filhos ou o bug de checkout que só acontece com uma regra de frete específica. Então os bancos de produção são copiados para o staging e para notebooks, e junto vão nomes de clientes, endereços, telefones, histórico de pedidos e todas as credenciais de API guardadas na configuração.
Essa cópia é um problema de proteção de dados sob a LGPD, o GDPR e leis parecidas, e também um risco operacional. Uma loja de staging com configurações de produção pode enviar e-mails reais para clientes, mandar pedidos de teste para o ERP ou capturar pagamentos. Sanitizar o banco resolve os dois. Este artigo mostra o que mudar e como automatizar.
Onde o trabalho acontece
Sanitize antes de os dados saírem de um ambiente controlado. O fluxo mais seguro é:
- Faça o dump da produção dentro da rede de produção.
- Importe num banco temporário num servidor que você controla, não no notebook de um dev.
- Rode o script de sanitização ali.
- Faça o dump do banco sanitizado. Esse arquivo é o que vai para o staging e para os devs.
Rodar o script depois que o dump bruto já foi copiado para cinco notebooks anula o objetivo. Os comandos de dump e import estão no guia de backup e restore.
Remover ou anonimizar
Existem duas abordagens, e a maioria dos projetos usa as duas.
Remover (strip) significa exportar a estrutura da tabela sem as linhas. Serve para tabelas que ninguém precisa no desenvolvimento: sessões, logs, relatórios, dados de visitantes, pedidos de reset de senha. O n98-magerun2 suporta isso direto com db:dump --strip, que aceita nomes de tabelas e grupos predefinidos. Rode n98-magerun2 db:dump --help para ver os grupos que a sua versão define.
n98-magerun2 db:dump --compression=gzip \
--strip="customer_visitor customer_log persistent_session password_reset_request_event oauth_token" \
staging.sql.gz
Anonimizar significa manter as linhas mas trocar os dados pessoais por valores falsos. Serve para clientes e pedidos, porque os devs precisam deles para reproduzir bugs na área da conta, no grid de pedidos, nas faturas e nas integrações.
Anonimizando clientes
Use valores determinísticos baseados no ID da entidade. São únicos, estáveis entre atualizações e fáceis de reconhecer como falsos.
UPDATE customer_entity SET
email = CONCAT('customer', entity_id, '@example.com'),
firstname = 'Test',
lastname = CONCAT('Customer', entity_id),
middlename = NULL,
dob = NULL,
taxvat = NULL,
password_hash = NULL,
rp_token = NULL,
rp_token_created_at = NULL;
UPDATE customer_address_entity SET
firstname = 'Test',
lastname = CONCAT('Customer', parent_id),
company = NULL,
street = CONCAT(entity_id, ' Test Street'),
telephone = '0000000000',
fax = NULL,
vat_id = NULL;
UPDATE newsletter_subscriber SET
subscriber_email = CONCAT('subscriber', subscriber_id, '@example.com');
Limpar o hash da senha significa que ninguém consegue logar como esses clientes com senha. Devs que precisam ver a conta de um cliente podem usar o recurso Login as Customer no admin, que vem com o Magento 2.4, ou resetar uma única conta de teste.
Mantenha cidade, região, CEP e país se as regras de frete e o cálculo de imposto dependem deles. Esses campos sozinhos raramente identificam uma pessoa, e mudá-los quebra o motivo de você ter copiado dados reais.
Anonimizando pedidos e quotes
Os pedidos carregam a própria cópia dos dados do cliente, separada da customer_entity. Mudar o cliente não muda os pedidos antigos.
UPDATE sales_order SET
customer_email = CONCAT('customer', entity_id, '@example.com'),
customer_firstname = 'Test',
customer_lastname = CONCAT('Order', entity_id),
customer_middlename = NULL,
customer_dob = NULL,
customer_taxvat = NULL,
remote_ip = NULL,
x_forwarded_for = NULL;
UPDATE sales_order_address SET
firstname = 'Test',
lastname = CONCAT('Order', parent_id),
email = CONCAT('customer', parent_id, '@example.com'),
company = NULL,
street = CONCAT(entity_id, ' Test Street'),
telephone = '0000000000',
vat_id = NULL;
UPDATE sales_order_grid SET
customer_email = CONCAT('customer', entity_id, '@example.com'),
customer_name = CONCAT('Test Order', entity_id),
billing_name = CONCAT('Test Order', entity_id),
shipping_name = CONCAT('Test Order', entity_id),
billing_address = NULL,
shipping_address = NULL;
UPDATE quote SET
customer_email = CONCAT('quote', entity_id, '@example.com'),
customer_firstname = 'Test',
customer_lastname = CONCAT('Quote', entity_id),
remote_ip = NULL;
UPDATE quote_address SET
firstname = 'Test',
lastname = 'Quote',
email = CONCAT('quote', quote_id, '@example.com'),
street = CONCAT(address_id, ' Test Street'),
telephone = '0000000000';
Os grids de invoice, shipment e credit memo também guardam nomes e endereços. Aplique o mesmo tipo de update em sales_invoice_grid, sales_shipment_grid e sales_creditmemo_grid, ou faça truncate e reconstrua com a indexação assíncrona de grids, se você a usa.
Confira também o sales_order_payment.additional_information, que os módulos de pagamento usam para guardar respostas do gateway, e qualquer tabela adicionada pelas suas extensões. Reviews (o nickname em review_detail), wishlists compartilhadas por e-mail, mensagens de presente e campos customizados de checkout podem guardar dados pessoais. Procure no seu schema por colunas chamadas email, phone, name, address ou ip.
SELECT TABLE_NAME, COLUMN_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND (COLUMN_NAME LIKE '%email%'
OR COLUMN_NAME LIKE '%phone%'
OR COLUMN_NAME LIKE '%telephone%'
OR COLUMN_NAME LIKE '%street%'
OR COLUMN_NAME LIKE '%remote_ip%')
ORDER BY TABLE_NAME;
Depois dos updates, reindexe o grid de clientes com bin/magento indexer:reindex customer_grid para que a lista de clientes do admin mostre os novos valores.
Neutralizando a configuração
Esta etapa evita efeitos colaterais reais. Uma loja de staging nunca deve enviar e-mail para um cliente real nem falar com uma conta de pagamento de produção.
UPDATE core_config_data SET value = 'https://staging.example.com/'
WHERE path IN ('web/unsecure/base_url', 'web/secure/base_url');
DELETE FROM core_config_data WHERE path IN ('web/cookie/cookie_domain');
TRUNCATE TABLE oauth_token;
Depois defina os valores pela CLI no servidor de staging, que também grava no escopo certo:
bin/magento config:set system/smtp/disable 1
bin/magento config:set design/search_engine_robots/default_robots "NOINDEX,NOFOLLOW"
bin/magento config:set google/analytics/active 0
Em lojas com vários websites, confira as base URLs em cada escopo de website também. Formas de pagamento, transportadoras, conectores de ERP e integrações com marketplaces têm cada um os próprios caminhos de configuração. Liste por projeto e coloque cada um em modo sandbox ou desative. Preste atenção especial às integrações que rodam pelo cron, porque começam a funcionar assim que o cron é ativado. Se você envia pedidos para um ERP, o staging deve apontar para o ambiente de teste do ERP ou para lugar nenhum. Os padrões estão no guia de exportação de pedidos.
Usuários do admin e acesso
A tabela admin_user guarda nomes e e-mails da equipe, e hashes de senha que alguém poderia tentar quebrar offline. No staging, mantenha só as contas das pessoas que trabalham naquele ambiente. Apague o resto, ou desative e troque os e-mails por falsos.
UPDATE admin_user SET
email = CONCAT('admin', user_id, '@example.com'),
is_active = 0
WHERE username NOT IN ('dev.anna', 'dev.mark');
Remova integrações que apontam para sistemas de produção, ou ao menos revogue os tokens delas, o que o TRUNCATE TABLE oauth_token acima já faz. Proteja o próprio site de staging com HTTP basic authentication ou restrição de IP no servidor web, para que mecanismos de busca e estranhos não o alcancem mesmo que a configuração de robots se perca na próxima atualização.
O que manter intocado
Sanitizar demais deixa a cópia inútil. Deixe o catálogo em paz: produtos, categorias, atributos, preços, estoque e imagens são dados do negócio, não dados pessoais, e são o motivo de os problemas de performance aparecerem no staging. Mantenha totais de pedido, itens, status, códigos de forma de pagamento e métodos de envio, para que relatórios, faturas e integrações se comportem como em produção. Mantenha cart price rules e cupons, a menos que tenham sido emitidos para pessoas identificadas. O objetivo é remover o que identifica uma pessoa ou dá acesso a um sistema, e nada além disso.
A questão da encryption key
Os segredos de produção na core_config_data são criptografados com a chave do env.php da produção. Se o staging usa a mesma chave, qualquer pessoa com acesso ao staging consegue descriptografar as credenciais de pagamento e API da produção. Dê ao staging a própria encryption key e sobrescreva os campos criptografados com credenciais de sandbox digitadas pelo admin ou pelo config:set. Valores criptografados com a chave antiga simplesmente vão falhar ao descriptografar no staging, que é o que você quer.
Automatize
Um processo de sanitização que depende de alguém lembrar dos passos vai ser pulado. Coloque o SQL num arquivo versionado no repositório, envolva dump, import, sanitize e export num único script e rode agendado ou pelo CI. Adicione uma checagem final que faz o script falhar se sobrar qualquer e-mail de cliente fora do example.com:
SELECT COUNT(*) FROM customer_entity WHERE email NOT LIKE '%@example.com';
Os devs então puxam o dump sanitizado mais recente para o ambiente local. Minha configuração para isso está no guia de Docker e Warden.
FAQ
É legal copiar o banco de produção do Magento para o staging?
Copiar dados pessoais para mais sistemas e pessoas aumenta as obrigações da LGPD, do GDPR e de leis parecidas. Anonimizar os dados de clientes e pedidos antes de a cópia sair da produção elimina a maior parte desse risco.
Como impeço o staging de enviar e-mails para clientes?
Defina system/smtp/disable como 1 com o bin/magento config:set e anonimize os e-mails. Faça os dois, porque um módulo pode enviar e-mail pelo próprio transport.
O staging deve usar a mesma encryption key da produção?
Não. Com a mesma chave, o staging consegue descriptografar os segredos da produção. Use uma chave nova e digite credenciais de sandbox no staging.
O n98-magerun2 consegue anonimizar dados?
O comando db:dump pode remover os dados de tabelas, o que tira as linhas mas mantém a estrutura. Para anonimizar as linhas que você mantém, rode o seu próprio SQL ou use uma ferramenta de anonimização dedicada.
Atendo como freelancer em Magento e Adobe Commerce, incluindo fluxos de staging e desenvolvimento. Se precisa de ajuda com isso, fale comigo.
Artigos relacionados
Backup e restore no Magento 2: o que salvar e como
Como fazer backup e restore de uma loja Magento 2: dumps de banco que importam sem erro, mídia, env.php e a chave de criptografia, cópias offsite e restores testados.
Ambiente local do Magento 2 com Docker e Warden
Monte um ambiente local do Magento 2.4.8 com Docker e Warden: env-init, o arquivo .env, instalar ou importar um projeto, Xdebug e os comandos do dia a dia.
Magento 2 em production mode: como mudar e fazer o deploy
O que o production mode do Magento 2 muda, como trocar com segurança e uma sequência de deploy para o 2.4.8 que tira compilação e static deploy do downtime.
Ajuste de MySQL no Magento 2: configurações e queries lentas
Ajuste MySQL e MariaDB para o Magento 2.4.8: buffer pool, redo log, tabelas temporárias, binlog e triggers, queries lentas e tabelas enxutas.