Voltar ao blog

LGPD em Aplicações Laravel: 8 Controles Que Resolvem 90% das Auditorias

Checklist técnico para aplicações Laravel atenderem LGPD na prática. Anonimização, soft delete, logs auditáveis, criptografia em repouso e os 8 controles que sempre aplico antes de qualquer sistema ir pra produção.

RS
Richard Sakaguchi Solution Architect

LGPD é a lei que mais virou meme no Brasil. Todo mundo fala, ninguém implementa direito, e quando a ANPD bate na porta o time descobre que o “termo de uso atualizado” não conta como controle técnico.

Esse artigo é o checklist que aplico em toda aplicação Laravel que entrego antes de subir pra produção. São 8 controles técnicos que, quando aplicados juntos, resolvem 90% do que vai aparecer em auditoria de LGPD. Os outros 10% são papelada do jurídico que não é meu departamento.

Por que isso virou prioridade em 2026

Multas da ANPD passaram de 47 casos em 2024 para mais de 380 em 2025. O valor médio aplicado triplicou. E o critério que pega quase todo mundo é o mesmo: ausência de medida técnica capaz de demonstrar boa-fé.

Boa-fé na LGPD não é discurso. É log, é criptografia, é processo de exclusão, é trilha auditável. Tudo isso é código.

Os 8 controles

1. Criptografia em repouso para dados sensíveis

Toda coluna que guarda CPF, CNPJ, RG, telefone, e-mail, endereço, dado bancário, dado de saúde passa por cast encrypted do Laravel ou por uma camada de cripto própria.

// app/Models/Cliente.php
class Cliente extends Model
{
    protected $casts = [
        'cpf' => 'encrypted',
        'rg' => 'encrypted',
        'telefone' => 'encrypted',
        'endereco' => 'encrypted:array',
    ];
}

Detalhe que muita gente esquece: o cast encrypted usa a APP_KEY. Se você perder a key, perdeu o dado. Backup da key em cofre separado é parte do procedimento, não nice-to-have.

2. Hash + busca por coluna espelho para campos que precisam ser pesquisáveis

Cripto resolve repouso, mas mata WHERE cpf = ?. Solução: coluna paralela com hash.

// Migration
$table->string('cpf')->nullable(); // encrypted
$table->string('cpf_hash', 64)->nullable()->index(); // SHA-256

// No model
public function setCpfAttribute($value)
{
    $this->attributes['cpf'] = $value;
    $this->attributes['cpf_hash'] = hash('sha256', preg_replace('/\D/', '', $value));
}

// Busca
Cliente::where('cpf_hash', hash('sha256', $cpfInput))->first();

Você ganha busca exata sem expor o dado em claro no índice do banco.

3. Soft delete + anonimização programada

Direito ao esquecimento (Art. 18, VI) exige que o titular consiga apagar o dado. Mas na contabilidade você tem obrigação fiscal de guardar por anos.

Solução: soft delete imediato + anonimização agendada após o prazo legal.

// app/Jobs/AnonymizeExpiredRecords.php
class AnonymizeExpiredRecords implements ShouldQueue
{
    public function handle(): void
    {
        Cliente::onlyTrashed()
            ->where('deleted_at', '<', now()->subYears(5))
            ->each(function ($cliente) {
                $cliente->update([
                    'nome' => 'ANONIMIZADO',
                    'cpf' => null,
                    'cpf_hash' => null,
                    'email' => null,
                    'telefone' => null,
                    'endereco' => null,
                    'anonimizado_em' => now(),
                ]);
            });
    }
}

Agendado no Kernel.php para rodar diariamente. Auditoria pede log, você mostra o job + a coluna anonimizado_em.

4. Log auditável de acesso a dado pessoal

Todo SELECT em dado sensível tem que deixar rastro de quem leu, quando, e por quê.

// app/Concerns/LogsPersonalDataAccess.php
trait LogsPersonalDataAccess
{
    public static function bootLogsPersonalDataAccess()
    {
        static::retrieved(function ($model) {
            if (auth()->check()) {
                DB::table('pii_access_log')->insert([
                    'user_id' => auth()->id(),
                    'model' => static::class,
                    'model_id' => $model->getKey(),
                    'context' => request()->route()?->getName(),
                    'ip' => request()->ip(),
                    'created_at' => now(),
                ]);
            }
        });
    }
}

Em models de cliente, paciente, beneficiário: use LogsPersonalDataAccess;. Acabou.

5. Política de retenção por tipo de dado

LGPD Art. 15: dado pessoal só pode ser tratado pelo tempo necessário à finalidade. Cada tipo tem prazo diferente:

Tipo de dadoPrazo legal típicoOnde guardar prazo
Currículo de candidato6 mesesdata_retention_months no model
Cliente ativoenquanto contratoflag is_active
Histórico fiscal5 anos após encerramentopurge_after data
Log de auditoria6 anosregra fixa no scheduler
Cookie de marketing12 mesesTTL na sessão

Tabela de política como source of truth: config/lgpd.php. Job diário verifica e processa.

6. Consentimento granular, versionado e revogável

accept_terms = true no users table não é consentimento válido. ANPD quer ver quando, qual versão, e como pode revogar.

// Migration
Schema::create('user_consents', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id');
    $table->string('purpose'); // marketing, comunicacao, terceiros, cookies
    $table->string('terms_version');
    $table->ipAddress('ip');
    $table->boolean('granted');
    $table->timestamp('granted_at');
    $table->timestamp('revoked_at')->nullable();
    $table->index(['user_id', 'purpose']);
});

Tela de “Minha privacidade” no app: usuário vê cada finalidade e pode revogar com 1 clique. Auditor pede prova: você mostra a linha com IP, timestamp e versão dos termos vigentes naquela data.

7. Mascaramento em logs e telas

Log nunca grava CPF, e-mail ou cartão em claro. Eu uso um middleware MaskSensitiveInLogs:

public function handle($request, Closure $next)
{
    $masked = $this->maskFields($request->all(), ['cpf', 'rg', 'senha', 'cartao']);
    Log::withContext(['request' => $masked]);
    return $next($request);
}

private function maskFields(array $data, array $fields): array
{
    foreach ($fields as $f) {
        if (isset($data[$f])) {
            $data[$f] = '***' . substr($data[$f], -3);
        }
    }
    return $data;
}

Frontend (Inertia/React) usa o mesmo padrão para mascaramento em listagens. CPF aparece como ***.***.***-12.

8. Exportação de dados pessoais sob demanda

Art. 18, II: titular pode pedir portabilidade dos próprios dados. Resposta em até 15 dias úteis.

Não dá pra resolver isso na unha quando o pedido chega. Tem que ter rota pronta:

// routes/api.php
Route::middleware(['auth'])->get('/privacidade/exportar', function () {
    $userData = [
        'perfil' => auth()->user()->only(['nome', 'email', 'telefone']),
        'pedidos' => auth()->user()->pedidos()->get()->toArray(),
        'consentimentos' => auth()->user()->consents()->get()->toArray(),
        'log_acessos' => auth()->user()->piiAccessLogs()->limit(1000)->get(),
    ];

    return response()->json($userData)
        ->header('Content-Disposition', 'attachment; filename=meus-dados.json');
});

Bonus: gerar PDF assinado digitalmente também atende o requisito.

O que esses controles NÃO resolvem

  • Política de privacidade desatualizada (departamento jurídico)
  • DPO designado (papelada formal)
  • Treinamento da equipe (RH + jurídico)
  • Análise de impacto à proteção de dados (DPIA) para tratamentos de alto risco (consultor LGPD)

Esses são processos, não código. Mas sem os 8 controles acima, nada disso te salva quando a ANPD pedir log de acesso a dado pessoal.

Resumo do que aplicar amanhã

  1. Cast encrypted em toda coluna sensível
  2. Coluna *_hash para busca exata
  3. Soft delete + job de anonimização programada
  4. Trait LogsPersonalDataAccess em models com PII
  5. Tabela de política de retenção + scheduler
  6. user_consents granular + versionado
  7. Middleware de mascaramento em logs
  8. Endpoint /privacidade/exportar por usuário autenticado

Todos os 8 cabem em uma sprint. Em um ERP que opero, esse pacote ficou pronto em 6 dias úteis de um dev sênior.

Quer um audit gratuito?

A gente faz revisão técnica de aderência LGPD em aplicações Laravel como serviço. Recebe relatório com cada controle marcado como Verde / Amarelo / Vermelho e prioridade de correção. Fale com nosso time.

Sakaguchi IA

Precisa colocar isso em produção?

Engenharia de software, IA aplicada e cibersegurança para empresas que operam de verdade. Fale com nosso time.

Falar com a equipe