Log de consentimento LGPD: a tabela MySQL que guarda a prova do aceite

Banner bonito, botão de aceitar funcionando, e o registro do aceite mora num cookie no navegador do visitante. Ele limpa o cache e a prova evapora. Numa cobrança da ANPD, aceite sem registro do seu lado é aceite que não existe.

O que resolve é uma tabela sua, no MySQL, gravada no instante em que o visitante clica:

-- troque wp_ pelo prefixo real do wp-config.php
CREATE TABLE wp_consentimento_lgpd (
  id            BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  visitante_id  CHAR(36)    NOT NULL,
  ip_hash       CHAR(64)    NOT NULL,
  versao_texto  VARCHAR(20) NOT NULL,
  categorias    VARCHAR(120) NOT NULL,
  acao          ENUM('aceitou','recusou','alterou') NOT NULL,
  criado_em     DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  KEY idx_visitante (visitante_id),
  KEY idx_criado (criado_em)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

O endpoint que o banner chama

add_action('rest_api_init', function () {
    register_rest_route('lgpd/v1', '/consentimento', [
        'methods'  => WP_REST_Server::CREATABLE,
        'permission_callback' => '__return_true',
        'callback' => function (WP_REST_Request $req) {
            global $wpdb;
            $ip = $_SERVER['REMOTE_ADDR'] ?? '';
            $ok = $wpdb->insert($wpdb->prefix . 'consentimento_lgpd', [
                'visitante_id' => sanitize_text_field((string) $req['visitante_id']),
                'ip_hash'      => hash_hmac('sha256', $ip, wp_salt('auth')),
                'versao_texto' => '2026-03',
                'categorias'   => sanitize_text_field((string) $req['categorias']),
                'acao'         => 'aceitou',
            ]);
            return ['ok' => (bool) $ok];
        },
    ]);
});

A rota é aberta de propósito, então qualquer um enfia linha na tabela: ponha um limite por IP no nginx antes de publicar.

Por que hash do IP e versão do texto

Guardar o IP cru é coletar dado pessoal para provar que você coleta pouco dado pessoal. O hash com sal do próprio site resolve os dois lados: dá para conferir um IP específico depois e a tabela sozinha não entrega ninguém. Só não troque os salts do wp-config, porque o wp_salt('auth') muda junto e os hashes antigos param de bater. E atrás de proxy ou Cloudflare o REMOTE_ADDR é o do proxy, pegue o IP do cabeçalho que ele assina.

A coluna versao_texto é a que salva a pele: você vai reescrever a política de privacidade e, sem ela, o log só diz que a pessoa aceitou alguma coisa. Some uma rotina de expurgo do que passou de cinco anos, com WHERE na data e dump feito antes.

Antes de subir, chame /wp-json/lgpd/v1/consentimento com curl -X POST e confira se a linha chegou. Endpoint de consentimento que falha calado é o pior tipo de bug.

Dúvidas? Faça um comentário logo abaixo ou envie uma mensagem clicando aqui.

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *