RLS desativado no Supabase: o que verificar antes de ativar
Um alerta vermelho do Security Advisor faz Ativar RLS parecer o primeiro passo óbvio. Em uma aplicação em produção, porém, esse clique pode bloquear leituras e gravações ao mesmo tempo. Adiar a correção mantém os dados expostos. Levante a finalidade da tabela, os privilégios atuais e as políticas existentes; depois, aplique as mudanças necessárias em uma única alteração testada.
Verifique quatro pontos antes de alterar a configuração
- A tabela está exposta pela Data API?
- Quais privilégios
GRANTos papéisanoneauthenticatedpossuem? - Alguma política existente abre o acesso de forma ampla com uma regra como
using (true)? - O navegador está usando uma chave secreta ou service-role no lugar de uma chave publicável?
RLS disabled in public significa que a segurança de nível de linha está desativada para uma tabela no esquema public. Um GRANT controla quais operações um papel pode tentar na tabela. O RLS restringe essas operações apenas para as linhas permitidas. Nenhuma das camadas substitui a outra.
RLS não esconde colunas. Se a mesma linha contiver endereços de e-mail, notas internas ou outros campos que não devem chegar ao navegador, não conceda permissões de tabela amplas SELECT. Use permissões de coluna, uma visão security_invoker do PostgreSQL 15 ou posterior que expõe apenas as colunas necessárias, ou uma API separada do servidor.
Não deduza os padrões do Supabase de 2026 apenas pela data de criação do projeto. Os novos padrões começaram a ser liberados para novos projetos em 30 de maio e estão programados para atingir projetos existentes em 30 de outubro. A mudança afeta as permissões padrão GRANT em tabelas criadas no futuro; não revoga automaticamente as permissões em tabelas existentes. Verifique diretamente a configuração do projeto e as permissões atuais.
1. Inspecione permissões e todas as políticas existentes
Abra Database → Security Advisor, identifique a tabela afetada e decida se ela deve ser legível antes do login, gravável apenas por usuários autenticados ou apenas no servidor. No Editor SQL, liste as políticas atuais:
select policyname, permissive, roles, cmd, qual, with_check
from pg_policies
where schemaname = 'public' and tablename = 'profiles';
Adicionar uma nova política não torna automaticamente o acesso mais restrito. Políticas permissivas múltiplas — o tipo padrão — combinam com OR. Uma antiga política using (true) ou to public pode, portanto, abrir acesso além de uma nova política por usuário. Remova ou restrinja políticas desnecessárias antes de adicionar outra. Se uma política com o nome pretendido já existir, inspecione sua definição; use ALTER POLICY ou uma migração DROP POLICY revisada em vez de reexecutar CREATE POLICY cegamente.
2. Prepare RLS, privilégios e políticas em conjunto
Este exemplo pressupõe que profiles.user_id armazene o ID do usuário autenticado. Ele permite que cada usuário autenticado selecione, insira, atualize e exclua as próprias linhas. Adapte a tabela, as colunas e as regras ao seu produto e teste primeiro no ambiente de staging.
begin;
alter table public.profiles enable row level security;
grant select, insert, update, delete
on table public.profiles to authenticated;
create policy "Users can view own rows"
on public.profiles for select
to authenticated
using ( (select auth.uid()) = user_id );
create policy "Users can insert own rows"
on public.profiles for insert
to authenticated
with check ( (select auth.uid()) = user_id );
create policy "Users can update own rows"
on public.profiles for update
to authenticated
using ( (select auth.uid()) = user_id )
with check ( (select auth.uid()) = user_id );
create policy "Users can delete own rows"
on public.profiles for delete
to authenticated
using ( (select auth.uid()) = user_id );
commit;
Em um UPDATE, using decide se a linha existente pode ser alterada, e with check valida a linha depois da mudança. O PostgreSQL reutiliza a condição using quando with check é omitido, mas explicitar as duas facilita a revisão da regra pretendida. O UPDATE também precisa de uma política SELECT que permita visualizar a linha.
Para uma tabela exclusiva do servidor, não invente uma política para o cliente. Em vez disso, revogue os papéis da Data API, por exemplo com revoke all on table public.profiles from anon, authenticated;. Se a aplicação precisar da tabela, conceda explicitamente apenas as operações necessárias. Caso uma operação INSERT use uma sequência diretamente, conceda separadamente o privilégio mínimo exigido para essa sequência. Para dados disponíveis antes do login, crie uma política anon específica e revise separadamente as linhas e as colunas públicas.
Tabelas criadas no Table Editor já vêm com RLS ativado. Nas tabelas criadas com SQL puro ou pelo SQL Editor, é preciso ativá-lo manualmente. Se a interface ficar vazia depois disso, as linhas provavelmente não foram apagadas; em geral, as solicitações estão sendo negadas porque não existe uma política ou um privilégio GRANT aplicável.
3. Migre cada consumidor de chave antes de desativar a chave legada
Navegadores devem usar uma chave publicável (sb_publishable_...). A chave anon de um projeto existente também foi criada para uso público, mas migrar para o novo sistema de chaves é preferível. Uma chave secreta (sb_secret_...) e a legada service_role contornam o RLS e devem permanecer fora dos clientes.
Criar uma nova chave não revoga a antiga. Migre na seguinte ordem:
- Substitua
anonpor uma chave publicável nos clientes web, mobile, desktop e já implantados. - Substitua
service_rolepor uma chave secreta em servidores, Edge Functions, workers, CI/CD, tarefas cron, integrações externas, Database Webhooks epg_net. - Verifique se alguma integração precisa enviar a nova chave secreta no cabeçalho
apikey, e não emAuthorization: Bearer. - Confirme que nenhuma solicitação ainda usa as credenciais antigas e desative as chaves legadas
anoneservice_roleemSettings → API Keys.
Se um valor sb_secret_... vazar, crie uma chave substituta, atualize o servidor e revogue a antiga. Trate o vazamento de uma chave legada service_role da mesma forma: não corrija apenas um caminho do código e encerre o trabalho. Conclua a migração e desative a credencial antiga. Versões anteriores do aplicativo, tarefas agendadas e webhooks podem falhar durante a transição; por isso, mantenha um inventário de todos os consumidores.
4. Teste com os papéis reais e dois usuários
No SQL Editor, select auth.uid(); normalmente retorna null, pois não há um JWT de usuário. SET LOCAL dura apenas para a transação atual, então coloque-o entre BEGIN e ROLLBACK. No código abaixo, o texto entre colchetes significa o UUID do usuário que você deseja testar:
begin;
set local role authenticated;
set local request.jwt.claim.sub = '<UUID of the user under test>';
select * from public.profiles;
rollback;
Esse SQL serve apenas como verificação rápida da política, não como teste final. Na aplicação, execute SELECT, INSERT, UPDATE e DELETE sem login, como usuário A e como usuário B. Confirme que o acesso do usuário A seja negado mesmo quando ele enviar diretamente o ID do usuário B, e que as colunas restritas nunca apareçam na resposta.
Coordene as políticas, os privilégios GRANT necessários e a implantação da aplicação como uma única e breve mudança em produção. Se encontrar uma política perigosamente abrangente, não a mantenha aberta por conveniência. Trate a correção como prioridade, valide-a no staging e prepare o SQL de rollback antes de restringir o acesso.