Home Projetos & Releases BigBangcraft Blog Sobre & Carreira
Arquitetura & CTO 7 min de leitura 12 visualizações
Raw .md

O erro de auditar prontuário gravando payload inteiro no PostgreSQL

PD

Pedro Dalben

CTO no Integrar Plus • Publicado em 22 de September de 2026

O erro de auditar prontuário gravando payload inteiro no PostgreSQL

Existe uma ilusão confortável no desenvolvimento de software de saúde: a ideia de que conformidade com a LGPD se resolve contratando um consultor jurídico e instalando uma gem de auditoria para gravar tudo o que passa pela aplicação. Em quase todas as discussões sobre o tema, vejo o debate se dividir em dois extremos que falham miseravelmente quando encontram o ambiente real de produção.

De um lado, estão os defensores da guarda irrestrita. O argumento dessa turma parece prudente à primeira vista: como o prontuário eletrônico tem valor probatório pericial e o Conselho Federal de Medicina exige guarda mínima de vinte anos, a aplicação deveria interceptar toda requisição HTTP, capturar headers, parâmetros e respostas, despejando tudo numa tabela de log ou num bucket de armazenamento para consumo posterior. Na visão desse grupo, quanto mais bytes você guardar, mais protegido você estará contra contestações judiciais.

Do outro lado, surge o purismo da minimização teórica. Esse segundo grupo leu o artigo sexto da LGPD, viu a exigência de coletar apenas o estritamente necessário para a finalidade e defende purgar qualquer registro técnico após trinta ou sessenta dias. Para eles, rastrear o endereço IP de quem abriu um exame ou manter histórico granular de alterações seria criar um passivo desnecessário de dados pessoais.

Os dois lados estão errados. O primeiro grupo transforma o banco de dados em um depósito tóxico de dados sensíveis desprotegidos. O segundo grupo destrói a cadeia de custódia do prontuário e coloca a clínica em risco jurídico imediato perante os conselhos de classe.

A armadilha do log cego em sistemas Rails

Quando você coloca um prontuário eletrônico no ar usando Ruby on Rails e PostgreSQL, a tentação de usar uma biblioteca padrão como audited ou paper_trail configurada no modo automático é gigantesca. O desenvolvedor define has_associated_audits, deixa a serialização padrão em JSON e segue em frente.

O desastre começa aí. Um prontuário multidisciplinar recebe anotações de evolução clínica, histórico familiar, hipóteses diagnósticas e prescrições. Se a sua trilha de auditoria apenas copia o payload serializado da linha inteira da tabela a cada save, qualquer correção de pontuação duplica parágrafos inteiros de texto livre contendo informações de saúde altamente sensíveis.

Pior ainda é a prática de registrar o payload da requisição web diretamente em arquivos de log do servidor ou tabelas de telemetria. Em pouco tempo, dados sobre diagnósticos psiquiátricos, medicações controladas e resultados de exames acabam gravados no disco da VPS, no log do Nginx, no journal do systemd e em cópias de segurança que deveriam guardar apenas métricas operacionais. Se um servidor desses for violado, o estrago é irreparável.

Eu prefiro tratar a auditoria clínica e a auditoria de acesso como subsistemas completamente distintos, inclusive no nível de modelagem relacional.

Separando acesso de mutação no PostgreSQL

A LGPD não exige que você deixe de registrar acessos; ela exige que você justifique o tratamento e limite o acesso a quem realmente precisa. Em saúde, ler um registro é tão crítico quanto alterá-lo. O vazamento de um prontuário costuma ocorrer por visualização indevida, não por edição fraudulenta.

No entanto, uma leitura não gera diff de tabela no ActiveRecord. Se você depende apenas de gatilhos de banco ou de callbacks no modelo, você não sabe quais profissionais abriram a ficha daquele paciente durante o plantão.

Para resolver isso sem saturar o banco, adoto uma tabela append-only dedicada exclusivamente aos eventos de visualização. Essa tabela não armazena o que o médico leu, mas sim as coordenadas do acesso:

CREATE TABLE prontuario_acessos (
    id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    prontuario_id bigint NOT NULL,
    usuario_id bigint NOT NULL,
    perfil_acesso text NOT NULL,
    ip_origem inet NOT NULL,
    user_agent text,
    data_hora timestamptz DEFAULT clock_timestamp() NOT NULL
) PARTITION BY RANGE (data_hora);

Perceba que essa tabela não guarda nomes, sintomas ou dados do paciente. Ela é composta quase inteiramente por chaves estrangeiras e metadados de rede. O particionamento mensal por data é indispensável: quando você lida com clínicas operando diariamente, essa tabela cresce milhões de linhas por ano em uma única VPS. Com partições bem desenhadas, você consegue arquivar blocos antigos ou aplicar índices sem travar a escrita em produção.

Já a auditoria de alterações exige outra postura. O prontuário precisa provar o que mudou, quando mudou e quem foi o autor, porque a adulteração de histórico médico é crime. Aqui, em vez de salvar a foto completa do registro a cada salvamento, o segredo é serializar exclusivamente o delta entre o valor anterior e o novo, restringindo as colunas auditadas:

class EvolucaoClinica < ApplicationRecord
  belongs_to :prontuario
  belongs_to :profissional

  # Apenas campos com relevância pericial entram na trilha de versão
  AUDITED_COLUMNS = %w[texto_evolucao conduta cid_codigo].freeze

  after_update :registrar_auditoria_clinica, if: :campos_criticos_alterados?

  private

  def campos_criticos_alterados?
    saved_changes.slice(*AUDITED_COLUMNS).any?
  end

  def registrar_auditoria_clinica
    alteracoes = saved_changes.slice(*AUDITED_COLUMNS).transform_values do |anterior, atual|
      { de: anterior, para: atual }
    end

    TrilhaAuditoriaClinica.create!(
      registro_id: id,
      registro_tipo: self.class.name,
      usuario_id: Current.usuario&.id,
      modificacoes: alteracoes,
      criado_em: Time.current
    )
  end
end

Guardar o hash de mudanças com chaves estritas impede que metadados internos de controle, como contadores de visualização ou marcas temporais de cache, poluam o histórico pericial.

O conflito de prazos entre a lei de saúde e a privacidade

Aqui está o ponto em que o consenso de mercado costuma ruir: o direito ao esquecimento e a eliminação de dados previstos na LGPD batem de frente com a Lei 13.787 de 2018 e as resoluções do CFM, que determinam guarda do prontuário pelo prazo de vinte anos a partir do último registro.

Muitos desenvolvedores ficam paralisados nessa encruzilhada. Alguns tentam implementar rotinas de deleção total atendendo a pedidos de pacientes; outros simplesmente ignoram os pedidos alegando que a lei médica atropela tudo.

Na prática operacional, a resposta técnica passa pela dissociação e minimização de dados periféricos. O prontuário em si, com anamnese, evoluções e laudos, fica retido sob a base legal de cumprimento de obrigação legal ou regulatória (artigo sétimo da LGPD). Ele não pode ser deletado durante os vinte anos, nem mesmo a pedido explícito do titular.

Por outro lado, o cadastro periférico do paciente, como dados de faturamento, cartões de crédito tokenizados, históricos de mensagens via WhatsApp e preferências de contato, não goza dessa mesma proteção temporal. Manter o número de telefone e o e-mail de um paciente inativo por duas décadas sob a justificativa de que ele possui um prontuário na clínica é erro crasso de arquitetura.

Quando um ciclo de atendimento se encerra e o prazo contratual expira, o sistema deve ser capaz de anonimizar os dados de contato do cadastro, mantendo o prontuário isolado, indexado apenas pelo identificador interno e associado ao documento de identificação original armazenado de forma criptografada.

O preço de auditar com sobriedade

Trabalhar com prontuário eletrônico em infraestrutura própria ensina rapidamente que complexidade desnecessária cobra pedágio em disco e em dor de cabeça jurídica. Se você tenta transformar o banco de dados em um espelho do tráfego de rede, você cria uma bomba de privacidade pronta para explodir na primeira falha de segurança.

A trilha de auditoria precisa ser tratada como um componente cirúrgico: metadados de acesso de um lado, diferenças pontuais de prontuário do outro, e zero poluição de dados sensíveis nos logs de aplicação do servidor. É essa disciplina na modelagem que mantém a aplicação rápida no PostgreSQL e garante que uma inspeção de conformidade não encontre prontuários espalhados em lugares onde eles nunca deveriam ter entrado.

PD

Sobre Pedro Dalben

CTO e arquiteto full stack especializado em Ruby on Rails, Vue.js, sistemas multi-tenant e infraestrutura distribuída.