← Voltar ao blog
PostgreSQL 23 de setembro de 2026 12 min de leitura

Segurança no PostgreSQL: conformidade, LGPD e auditoria

scram-sha-256, TLS obrigatório, o perigo do schema public, Row Level Security, auditoria com pgaudit e a pergunta que a LGPD faz: quem leu esse dado, quando e por quê. Com SQL pronto para usar.

Segurança no PostgreSQL: conformidade, LGPD e auditoria

Auditoria de segurança em banco de dados costuma ter dois tempos. O primeiro é o time dizendo que está tudo certo. O segundo é eu rodando cinco consultas e a sala ficando em silêncio. Vamos direto às cinco.

Cofre de dados com camadas de autenticação, criptografia e trilha de auditoria ao redor de um banco de dados
Segurança em banco é camada. Falhou uma, as outras ainda seguram.

1. Autenticação: md5 ainda está lá?

O método md5 é obsoleto e quebrável. O padrão é scram-sha-256 desde o PostgreSQL 10, e mesmo assim eu ainda encontro produção com md5 porque "veio assim da migração".

SHOW password_encryption;    -- tem que ser scram-sha-256

-- Quem ainda tem senha em md5
SELECT rolname FROM pg_authid WHERE rolpassword LIKE 'md5%';

ALTER SYSTEM SET password_encryption = 'scram-sha-256';
SELECT pg_reload_conf();
-- Atencao: cada usuario listado precisa redefinir a senha para migrar o hash
ALTER ROLE app_web PASSWORD 'nova-senha-forte';

No pg_hba.conf, a ordem das linhas é lei — a primeira que casa vence. Use hostssl, nunca trust, e faixas de rede estreitas:

# TIPO    BANCO   USUARIO       ORIGEM           METODO
hostssl   app     app_web       10.20.0.0/24     scram-sha-256
hostssl   all     dba_admin     10.99.0.5/32     scram-sha-256
host      all     all           0.0.0.0/0        reject

2. TLS: criptografado mesmo, ou só configurado?

-- Quem esta conectado sem TLS agora
SELECT a.usename, a.client_addr, a.application_name, s.ssl, s.version, s.cipher
FROM pg_stat_activity a
LEFT JOIN pg_stat_ssl s USING (pid)
WHERE a.backend_type = 'client backend'
ORDER BY s.ssl NULLS FIRST;

Configurar ssl = on não obriga ninguém. A obrigação vem do hostssl no pg_hba. E no lado da aplicação, sslmode=require criptografa mas não valida o certificado — para proteger contra man-in-the-middle é verify-full.

3. O schema public: a porta que fica aberta

Até o PostgreSQL 14, qualquer usuário conectado podia criar objetos no schema public. O 15 corrigiu o padrão para bancos novos, mas banco migrado carrega a herança. Confira:

REVOKE CREATE ON SCHEMA public FROM PUBLIC;
REVOKE ALL ON DATABASE app FROM PUBLIC;

-- Privilegio minimo de verdade, com roles de grupo
CREATE ROLE app_leitura NOLOGIN;
GRANT USAGE ON SCHEMA vendas TO app_leitura;
GRANT SELECT ON ALL TABLES IN SCHEMA vendas TO app_leitura;
ALTER DEFAULT PRIVILEGES IN SCHEMA vendas
  GRANT SELECT ON TABLES TO app_leitura;

CREATE ROLE relatorio LOGIN PASSWORD 'xxxx' IN ROLE app_leitura;

Sem o ALTER DEFAULT PRIVILEGES, toda tabela nova nasce invisível e alguém "resolve" com um GRANT amplo. É assim que permissão apodrece.

E o clássico: a aplicação conecta como superusuário. Descubra quem:

SELECT rolname, rolsuper, rolcreaterole, rolcreatedb, rolbypassrls
FROM pg_roles WHERE rolcanlogin AND (rolsuper OR rolcreaterole OR rolbypassrls);

4. Row Level Security: isolamento que o ORM não burla

RLS aplica o filtro no banco. Se o desenvolvedor esquecer o WHERE tenant_id, o banco lembra por ele.

ALTER TABLE contratos ENABLE ROW LEVEL SECURITY;
ALTER TABLE contratos FORCE ROW LEVEL SECURITY;   -- vale ate para o dono

CREATE POLICY p_contratos_tenant ON contratos
  FOR ALL TO app_web
  USING      (tenant_id = current_setting('app.tenant_id')::bigint)
  WITH CHECK (tenant_id = current_setting('app.tenant_id')::bigint);

A aplicação define o contexto no início da transação:

BEGIN;
SELECT set_config('app.tenant_id', '42', true);  -- true = escopo da transacao
SELECT * FROM contratos;                          -- so ve o tenant 42
COMMIT;

Dois avisos que custam caro: USING controla leitura e WITH CHECK controla escrita — sem o segundo, dá para inserir linha de outro tenant. E roles com BYPASSRLS ou superusuário ignoram tudo; é por isso que FORCE existe. Vale lembrar que RLS tem custo no plano: a policy vira predicado, então indexe a coluna do tenant.

5. Auditoria: a pergunta da LGPD

A LGPD não pergunta se o dado está seguro. Ela pergunta quem acessou, quando, para qual finalidade e por quanto tempo você guarda. Log de erro não responde nada disso. O caminho é pgaudit:

-- shared_preload_libraries = 'pgaudit'  (exige restart)
CREATE EXTENSION IF NOT EXISTS pgaudit;

ALTER SYSTEM SET pgaudit.log = 'ddl, role, write';
ALTER SYSTEM SET pgaudit.log_parameter = on;
ALTER SYSTEM SET pgaudit.log_relation = on;

-- Auditoria dirigida: so o que toca dado pessoal
CREATE ROLE auditor NOLOGIN;
ALTER SYSTEM SET pgaudit.role = 'auditor';
GRANT SELECT, UPDATE, DELETE ON clientes, contratos TO auditor;
SELECT pg_reload_conf();

Auditar read em tudo gera volume absurdo de log e ninguém lê. Auditar SELECT só nas tabelas com dado pessoal, via pgaudit.role, é o equilíbrio que sobrevive ao primeiro mês.

Anonimização e minimização

-- Visao com mascaramento para ambiente de analise
CREATE VIEW clientes_mascarado AS
SELECT id,
       left(nome, 1) || repeat('*', 6)                       AS nome,
       regexp_replace(cpf, '^(\d{3})\d{6}(\d{2})
    
    
  

, '\1******\2') AS cpf,
       regexp_replace(email, '(^.).*(@.*$)', '\1****\2')     AS email,
       cidade, uf
FROM clientes;

REVOKE ALL ON clientes FROM analista;
GRANT SELECT ON clientes_mascarado TO analista;

Para ambiente de desenvolvimento, a extensão pg_anonymizer faz mascaramento dinâmico por role. E lembre do direito à eliminação: precisa existir um procedimento de expurgo, o que combina com particionamento por data — assunto de VACUUM e bloat.

Criptografia em repouso e o backup esquecido

O PostgreSQL não tem TDE nativo na versão comunitária. Na prática se resolve com criptografia de volume (LUKS, ou o disco criptografado do provedor) mais criptografia de colunas sensíveis com pgcrypto quando a chave precisa ficar fora do banco.

E o ponto que mais vejo falhar em auditoria: o banco está impecável e o backup está num bucket sem criptografia, sem rotação, com acesso amplo. Backup é cópia integral do dado pessoal. Ele entra no mesmo escopo de conformidade, com a mesma restrição e o mesmo prazo de retenção. E se ele está replicado para outro site, releia alta disponibilidade e replicação pensando em quem tem acesso à réplica.

Checklist rápido

  • scram-sha-256 em todas as roles, nenhum md5 remanescente
  • hostssl no pg_hba, verify-full no cliente
  • Nenhuma aplicação conectando como superusuário
  • CREATE revogado do schema public, roles de grupo com default privileges
  • RLS com FORCE nas tabelas multi-tenant
  • pgaudit dirigido às tabelas com dado pessoal, log fora do servidor
  • Backup criptografado, com retenção definida e restauração testada
  • Patches de minor version aplicados — a maioria das CVEs corrigidas mora aí

O panorama completo está no guia definitivo de PostgreSQL em produção. Se você também administra MySQL, o comparativo de maturidade está em MySQL ou PostgreSQL.


Sobre o autor: Alexandre Almeida atua com bancos de dados há mais de 25 anos, foi instrutor oficial de MySQL e Oracle para mais de 1.000 alunos e escreveu Aprendendo MySQL: Prática, Teoria e Laboratórios. Exemplos validados em PostgreSQL 16 e 17.

Fontes: PostgreSQL Documentation — Client Authentication, SSL Support, Row Security Policies e Privileges; documentação das extensões pgaudit e pgcrypto; Lei nº 13.709/2018 (LGPD), artigos 6º, 16 e 46.