Banco de Dados

25 mar, 2019

Anonimizando base MySQL com Python

Publicidade

Você, assim como eu, já deve ter passado por situações em que você precisava de uma base dados para subir o seu projeto similar ao ambiente de produção, localmente.

O problema de gerar uma replica do banco de dados de produção para ser executado localmente é bem tranquilo de se resolver.

O que é um pouco mais complexo é gerar uma base sem as informações sigilosas dos clientes, e isso pode colocar mais um grau de complexidade, e talvez, se tiver que seguir um compliance, você terá mais um grau a ser respeitado.

Baseado nas premissas que existiam, o time que eu fazia parte começou uma pesquisa antes de sair criando qualquer coisa, e chegamos ao mysql-anonymous – simples e rápido de utilizar.

Bastava apenas configurar um YAML, e já teríamos o dump que precisávamos, sem dependência externa, sem precisar se conectar ao banco e nem à internet para gerar o SQL.

O único problema é que o projeto não recebia nenhuma atualização desde 2015 e não tinha nenhum teste para validar o processo de construção.

Como renasce uma fênix

O que eu acho muito legal em trabalhar com open source é que você pode fazer o seu fork e dar vida àquilo que ainda é funcional, mas que precisa de alguém para cuidar.

O mysql-anonymous ganhou um segunda oportunidade, agora com toda uma cobertura de teste, ci, coverage e requires para garantir que todo o package é funcional.

E além de adicionar isso, colocamos algumas features legais que não existiam antes. Ele ainda não está no pypi – é o próximo passo. Para instalar basta você executar o comando abaixo:

pip install https://github.com/riquellopes/mysql-anonymous/tarball/master

Como gerar seu primeiro dump

Após instalar o seu mysql-anonymous, se você já quiser ver um exemplo de dump sendo gerado, basta digitar o comando anonymize -sample-one, e você terá um dump como o que você vai ver abaixo e logo após o YML que deu origem a ele:

--
SET @common_hash_secret=rand();

SET FOREIGN_KEY_CHECKS=0;
TRUNCATE `addonlogs`;
TRUNCATE `api_auth_tokens`;
TRUNCATE `approvals`;
TRUNCATE `auth_group`;
TRUNCATE `auth_group_permissions`;
TRUNCATE `auth_message`;
TRUNCATE `auth_permission`;
TRUNCATE `auth_user_groups`;
TRUNCATE `auth_user_user_permissions`;
TRUNCATE `auth_user`;
TRUNCATE `cache`;
TRUNCATE `cake_sessions`;
TRUNCATE `collections_tokens`;
TRUNCATE `django_admin_log`;
TRUNCATE `django_session`;
TRUNCATE `django_site`;
TRUNCATE `download_counts`;
TRUNCATE `eventlog`;
TRUNCATE `facebook_data`;
TRUNCATE `facebook_detected`;
TRUNCATE `facebook_favorites`;
TRUNCATE `facebook_sessions`;
TRUNCATE `facebook_users`;
TRUNCATE `favorites`;
TRUNCATE `global_stats`;
TRUNCATE `hubrsskeys`;
TRUNCATE `log_activity`;
TRUNCATE `log_activity_addon`;
TRUNCATE `log_activity_user`;
TRUNCATE `piston_consumer`;
TRUNCATE `piston_nonce`;
TRUNCATE `piston_token`;
TRUNCATE `reviewratings`;
TRUNCATE `reviews_moderation_flags`;
TRUNCATE `sphinx_index_feed_tmp`;
TRUNCATE `stats_addons_collections_counts`;
TRUNCATE `stats_collections`;
TRUNCATE `stats_collections_counts`;
TRUNCATE `stats_collections_share_counts`;
TRUNCATE `stats_collections_share_counts_totals`;
TRUNCATE `stats_contributions`;
TRUNCATE `stats_share_counts`;
TRUNCATE `stats_share_counts_totals`;
TRUNCATE `subscription_events`;
TRUNCATE `update_counts`;
TRUNCATE `users_versioncomments`;
TRUNCATE `versioncomments`;
DELETE FROM `collections` WHERE `listed` = "0";
DELETE FROM `addons_users` WHERE `listed` = "0";
DELETE FROM `config` WHERE `value` = "emailchange_secret";
UPDATE `compatibility_reports` SET `client_os` = NULL, `comments` = NULL, `client_ip` = INET_NTOA(RAND()*1000000000);
UPDATE `versions` SET `approvalnotes` = NULL;
UPDATE `reviews` SET `ip_address` = INET_NTOA(RAND()*1000000000);
UPDATE `collections` SET `downloads` = ROUND(RAND()*1000000), `weekly_subscribers` = ROUND(RAND()*1000000), `monthly_subscribers` = ROUND(RAND()*1000000);
UPDATE `blacklisted_guids` SET `comments` = NULL;
UPDATE `addons` SET `nominationmessage` = NULL, `paypal_id` = NULL, `charity_id` = NULL, `average_daily_downloads` = ROUND(RAND()*1000000), `average_daily_users` = ROUND(RAND()*1000000), `total_contributions` = ROUND(RAND()*1000000) WHERE id NOT IN(556, 889);
UPDATE `users` SET `firstname` = NULL, `lastname` = NULL, `password` = NULL, `confirmationcode` = NULL, `resetcode` = NULL, `resetcode_expires` = NULL, `notes` = NULL, `last_login_ip` = NULL, `last_login_ip` = NULL, `last_login_attempt` = NULL, `last_login_attempt_ip` = NULL, `failed_login_attempts` = NULL, `email` = CONCAT(id, '@example.com'), `username` = CONCAT('_user_', id), `nickname` = CONCAT('_user_', id);
SET FOREIGN_KEY_CHECKS=1;
# This is a sample anonymize.yml file that's used for the Firefox Add-ons
# database.

database:
    truncate:
        - addonlogs
        - api_auth_tokens
        - approvals
        - auth_group
        - auth_group_permissions
        - auth_message
        - auth_permission
        - auth_user_groups
        - auth_user_user_permissions
        - auth_user
        - cache
        - cake_sessions
        - collections_tokens
        - django_admin_log
        - django_session
        - django_site
        - download_counts
        - eventlog
        - facebook_data
        - facebook_detected
        - facebook_favorites
        - facebook_sessions
        - facebook_users
        - favorites
        - global_stats
        - hubrsskeys
        - log_activity
        - log_activity_addon
        - log_activity_user
        - piston_consumer
        - piston_nonce
        - piston_token
        - reviewratings
        - reviews_moderation_flags
        - sphinx_index_feed_tmp
        - stats_addons_collections_counts
        - stats_collections
        - stats_collections_counts
        - stats_collections_share_counts
        - stats_collections_share_counts_totals
        - stats_contributions
        - stats_share_counts
        - stats_share_counts_totals
        - subscription_events
        - update_counts
        - users_versioncomments
        - versioncomments
    tables:
        addons:
            exception:
                - 556
                - 889
            nullify: [nominationmessage, paypal_id, charity_id]
            random_int:
                - average_daily_downloads
                - average_daily_users
                - total_contributions
        addons_users:
            delete:
                listed: 0
        blacklisted_guids:
            nullify: comments
        collections:
            delete:
                listed: 0
            random_int: [downloads, weekly_subscribers, monthly_subscribers]
        compatibility_reports:
            random_ip: client_ip
            nullify: [client_os, comments]
        config:
            delete:
                value: "emailchange_secret"
        reviews:
            random_ip: ip_address
        users:
            random_email: email
            nullify:
                - firstname
                - lastname
                - password
                - confirmationcode
                - resetcode
                - resetcode_expires
                - notes
                - last_login_ip
                - last_login_ip
                - last_login_attempt
                - last_login_attempt_ip
                - failed_login_attempts
            random_username: [username, nickname]
        versions:
            nullify: approvalnotes

Quando estiver preparado para gerar o seu próprio YML, basta executar o comando abaixo:

$ anonymize -y file_name.yml

Na documentação você vai encontrar todos os tipos de anonimização, mas vou deixar aqui os que foram adicionamos por nós, já que são muito específicos do nosso país.

Ajuda é sempre bem vinda

Se você nunca colaborou com um projeto open source, essa é a sua oportunidade! Entre, faça um fork, proponha melhorias, aumente a cobertura de teste, enfim, sempre existe algo a melhorar.