securitypostgres

RLS no Postgres: 'Ativado, Sem Política' é Deny-All para Backend

· 5 min de leitura

Aqui na MoWave One, a gente está sempre refinando nossa arquitetura de backend para o app Lima. Uma auditoria de segurança recente destacou um detalhe importante de configuração no nosso banco de dados Postgres rodando no Supabase: a maneira correta de proteger tabelas exclusivas do backend usando Row Level Security (RLS). Isso nos levou a uma compreensão mais clara de que RLS ativado com sem políticas não é uma má configuração, mas sim uma estratégia poderosa de “negar por padrão” para dados gerenciados exclusivamente pelo nosso backend Spring.

O Problema: RLS Desabilitado no Schema Public

Nosso consultor de segurança do Supabase recentemente sinalizou cinco tabelas no nosso schema public como rls_disabled_in_public. Essas tabelas eram: ai_conversation_meta, note_attachments, finance_goal_adjustments, finance_recurring_open_series, e community_post_images. À primeira vista, isso pode parecer uma vulnerabilidade de segurança óbvia. A preocupação principal é que a chave anon está embutida na nossa aplicação mobile. O serviço PostgREST do Supabase, que expõe nossa API de banco de dados, por padrão concede acesso a qualquer tabela no schema public que tenha o RLS desabilitado, permitindo que a chave anon, e por extensão, qualquer cliente não autenticado, consulte ou manipule essas tabelas. Este é um risco de exposição significativo, pois essas tabelas específicas contêm dados sensíveis de usuários e metadados operacionais que nunca deveriam ser acessíveis diretamente do lado do cliente.

A Causa Técnica: Tabelas Exclusivas do Backend Expostas

O cerne da questão não era que tínhamos dados sensíveis, mas sim que essas cinco tabelas nunca foram feitas para acesso direto do cliente. Elas são 100% gerenciadas pelo backend. Nosso serviço de backend Spring é o único responsável por todas as operações de leitura e escrita nelas. O role anon, que representa usuários não autenticados, e até mesmo o role authenticated, para usuários logados que não receberam acesso específico à tabela, não deveriam ter privilégios absolutamente nenhum nessas tabelas. Quando o RLS está desabilitado em uma tabela, o Postgres efetivamente ignora quaisquer permissões de nível de linha para todos os roles, tornando a tabela totalmente aberta para qualquer um com privilégios de nível de schema de SELECT, INSERT, UPDATE ou DELETE. Para tabelas no schema public, esses privilégios de nível de schema são frequentemente concedidos amplamente a roles como anon e authenticated por padrão em uma configuração Supabase, criando a exposição exata que o consultor de segurança estava destacando.

A Correção: RLS Ativado, Sem Políticas Anexadas

Nossa solução foi habilitar o RLS em todas as cinco tabelas sinalizadas, mas, crucialmente, não anexamos nenhuma política a elas. Isso pode soar contraintuitivo para quem é novo em RLS, já que o uso comum envolve a definição de políticas para conceder acesso específico. No entanto, para tabelas exclusivas do backend, RLS ativado combinado com sem políticas equivale a uma regra deny-all para qualquer role que não tenha privilégios de BYPASSRLS.

Veja como aplicamos a correção:

ALTER TABLE public.ai_conversation_meta ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.note_attachments ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.finance_goal_adjustments ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.finance_recurring_open_series ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.community_post_images ENABLE ROW LEVEL SECURITY;

Essa simples instrução ALTER TABLE foi executada para cada uma das cinco tabelas. Depois de habilitar o RLS, sem nenhuma instrução CREATE POLICY, os roles anon e authenticated foram imediatamente negados a qualquer acesso a essas tabelas. Isso fechou efetivamente a potencial lacuna de segurança identificada pelo consultor do Supabase.

Agora, vamos considerar os caminhos de leitura e escrita para o nosso backend. O backend Spring do app Lima se conecta ao Postgres como o role postgres. Essa conexão é estabelecida através do pooler Supavisor, que gerencia nossas conexões de banco de dados de forma eficiente. O role postgres, sendo um role de superusuário, automaticamente possui privilégios BYPASSRLS. Isso significa que, mesmo com RLS ativado e sem políticas, nosso backend pôde continuar a ler e escrever nessas tabelas exatamente como fazia antes, sem nenhuma interrupção. O atributo BYPASSRLS garante que o role postgres esteja isento das verificações de RLS, preservando a funcionalidade do nosso backend enquanto protege as tabelas de acesso externo não autorizado.

Essa mudança também alinhou essas cinco tabelas com nosso padrão existente para tabelas exclusivas do backend. A gente já tinha aproximadamente 40 outras tabelas configuradas com RLS-ativado-com-sem-política, um padrão que usamos para todos os dados que devem ser gerenciados exclusivamente pelos nossos serviços. A implementação foi simples; aplicamos diretamente em produção através do Painel de Controle de Gerenciamento do Supabase (MCP) e registramos como migração V103 em nosso sistema de controle de versão. Monitoramos o sistema de perto, e o healthcheck dos nossos serviços permaneceu UP durante e após a mudança, confirmando que as operações do nosso backend não foram afetadas.

Pontos Chave: RLS-on-no-policy é Seguro por Padrão

Essa experiência reforçou um conceito fundamental: RLS-ativado-sem-política não é uma má configuração, mas uma configuração perfeitamente válida e segura para tabelas que devem ser gerenciadas exclusivamente por um backend confiável. Ela estabelece uma postura de segurança de negar por padrão para todos os roles que não possuem privilégios BYPASSRLS. A nota do consultor de segurança do Supabase sobre “ativado, sem política” deve ser interpretada como informativa, e não como um indicador de bug ou estado inseguro, principalmente ao lidar com tabelas exclusivas do backend. Ela simplesmente informa que o RLS está ativo e, sem políticas explícitas, o acesso é restrito.

Para a MoWave One e o app Lima, essa abordagem simplifica nosso modelo de segurança. Ela nos dá confiança de que dados sensíveis, mesmo que residam no schema public, estão protegidos contra acesso indesejado do cliente, enquanto nosso backend mantém controle operacional total. Esta é uma lição importante na construção de aplicações seguras, como a que você pode experimentar em https://getlima.app. Sempre considere os roles e seus privilégios ao configurar RLS, e lembre-se que, às vezes, a política mais segura é não ter política nenhuma.