DataARTIGO

Anonimizando base MySQL com Python

No seguinte artigo, Henrique Lopes apresenta um passo a passo ensinando a anonimizar uma base MySQL com a linguagem de programação Python.

Anonimizando base MySQL com Python
Imagem: Henrique Lopes

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:

bash
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:

bash
$ 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.

Software developer na concrete, formado em Análise de Sistemas. Com experiência em python, nodejs, php, e go. Quando tem um tempo livre gosta de se dedicar a pequenos projetos em sua marcenaria.

Ver perfil