Buckets públicos no Supabase Storage: os arquivos dos seus usuários estão expostos?
Avatares podem ser públicos; documentos, notas fiscais e contratos, não. Como revisar os buckets e as regras do Supabase Storage e corrigir sem quebrar nada.
4 min de leitura
A maioria dos apps feitos com Lovable, Bolt e ferramentas parecidas deixa o usuário enviar alguma coisa: uma foto de perfil, um comprovante, um contrato, a foto de um documento para verificação. No Supabase, esses arquivos vão para o Storage, organizados em buckets.
Quando uma ferramenta de IA cria um bucket, muitas vezes ela o deixa público, porque assim as imagens aparecem sem trabalho extra. Para um logo, tudo bem. Para qualquer coisa pessoal, significa que qualquer pessoa com o link baixa o arquivo, e às vezes qualquer pessoa consegue listar todos os arquivos do bucket.
Este guia é a metade “Storage” da nossa verificação de 10 minutos no Supabase. Leva mais ou menos o mesmo tempo.
Buckets públicos e privados
| Bucket público | Bucket privado (o padrão) | |
|---|---|---|
| Baixar um arquivo | Qualquer pessoa com a URL, sem checar regras | Só se uma regra permitir, ou com um link assinado que expira |
| Enviar, apagar, mover | Controlado pelas suas regras | Controlado pelas suas regras |
| Listar os arquivos | Controlado pelas suas regras | Controlado pelas suas regras |
Duas coisas decorrem dessa tabela:
- Um bucket público ignora completamente as suas regras nos downloads. Se os caminhos dos arquivos são previsíveis (
invoices/1043.pdf,ids/user-15.jpg), um estranho consegue adivinhá-los. - Deixar o bucket privado não adianta se as regras em
storage.objectsdeixam qualquer um ler tudo. As regras do Storage são políticas comuns de Row Level Security nessa tabela, e uma regra generosa abre um bucket privado do mesmo jeito.
Passo 1: liste seus buckets
No painel do Supabase, abra o SQL Editor e rode:
select id, public, file_size_limit, allowed_mime_types
from storage.buckets
order by public desc, id;Para cada linha com public = true, faça uma pergunta: eu ficaria tranquilo se todos os arquivos deste bucket aparecessem num buscador? Imagens de produtos, sim. Qualquer coisa que um usuário enviou sobre si mesmo, não.
Olhe também as duas últimas colunas. Limites vazios significam que qualquer pessoa autorizada a enviar pode guardar um arquivo de qualquer tamanho e qualquer tipo no seu projeto, por sua conta.
Passo 2: leia as regras do Storage
select policyname, cmd, roles, qual, with_check
from pg_policies
where schemaname = 'storage' and tablename = 'objects'
order by cmd, policyname;O que procurar:
- Uma regra
SELECTparaanonoupubliccuja condição é sóbucket_id = '…'. Ela deixa qualquer pessoa listar e ler todos os arquivos do bucket. - Uma regra
INSERTparaanon, ou paraauthenticatedsem condição de pasta. Ela deixa qualquer usuário enviar arquivos em qualquer lugar, inclusive por cima dos caminhos de outros usuários, se uma regraUPDATEtambém estiver aberta. - Uma regra
DELETEque não liga o arquivo ao seu dono.
O Security Advisor do painel também aponta alguns desses casos. Confira depois de qualquer mudança.
Passo 3: confirme de fora
Usando só a URL do projeto e a chave pública (em Project Settings → API Keys), tente listar um bucket como um estranho faria:
curl -X POST "https://YOUR-PROJECT.supabase.co/storage/v1/object/list/BUCKET_NAME" \
-H "apikey: YOUR_PUBLIC_KEY" \
-H "Content-Type: application/json" \
-d '{"prefix": "", "limit": 20}'Se isso devolver nomes de arquivos de um bucket que guarda arquivos privados, a listagem está aberta. Repita para cada bucket que guarde algo pessoal.
Como corrigir
Leve os arquivos pessoais para um bucket privado
Crie um bucket privado (por exemplo, documents) para tudo que identifica uma pessoa ou contém dados do negócio. Mantenha um bucket público só para arquivos que devem mesmo ser públicos.
Depois, mude a forma como o app mostra esses arquivos. Em vez de uma URL pública permanente, ele pede ao Supabase uma URL assinada que expira, por exemplo em uma hora. No cliente JavaScript, isso é createSignedUrl(path, 3600). Os links públicos antigos param de funcionar quando você troca, então planeje essa mudança junto com a mudança no código, não antes.
Dê a cada usuário a sua própria pasta
O padrão mais comum é enviar cada arquivo dentro de uma pasta com o ID do usuário, como documents/8f1c…/contract.pdf, e escrever regras que conferem isso:
create policy "Read own documents"
on storage.objects for select
to authenticated
using (
bucket_id = 'documents'
and (storage.foldername(name))[1] = (select auth.uid()::text)
);
create policy "Upload to own folder"
on storage.objects for insert
to authenticated
with check (
bucket_id = 'documents'
and (storage.foldername(name))[1] = (select auth.uid()::text)
);Adicione regras de update e delete com a mesma condição se os usuários puderem substituir ou remover seus arquivos. Enviar com a opção de sobrescrever ligada exige select, insert e update.
Buckets públicos: leitura sim, listagem não
Um bucket público de avatares não precisa de nenhuma regra SELECT para os downloads, já que as URLs públicas ignoram as regras. Remova regras SELECT amplas para anon em buckets públicos, a menos que o app realmente precise listar o conteúdo deles.
Defina limites
Dê a cada bucket um tamanho máximo de arquivo e uma lista de tipos permitidos (por exemplo, image/png, image/jpeg, application/pdf). Leva um minuto nas configurações do bucket e impede que o seu Storage vire hospedagem grátis para outra pessoa.
Se o seu app lida com documentos, identidades ou registros de pagamento e você quer que alguém olhe com você, o nosso Security Check gratuito revisa o Storage junto com as regras do banco, as chaves e a autenticação, sem alterar nada e de fora.
SupabaseRow Level SecurityApps feitos com IA
Continue lendo
Todos os artigos
Segurança de apps
Antes de lançar um app feito no Lovable: checklist de segurança
Quinze verificações antes de chegarem usuários e dados reais: regras do banco, chaves, storage, login, pagamentos, funções e backups. Quase todas em minutos.
4 min de leitura

Segurança de apps
Chaves da OpenAI ou da Stripe no seu app feito com IA? Leve-as para uma função no servidor
Qualquer pessoa copia uma chave do frontend. Quais podem ficar no navegador, como achar as que não podem e como levá-las para uma Edge Function do Supabase.
4 min de leitura

Segurança de apps
Achou a chave secreta do Supabase no navegador? O que fazer na primeira hora
Uma chave service role ou secreta no frontend dá a qualquer pessoa acesso total ao banco. A ordem certa: revogar, substituir, olhar os logs e corrigir a causa.
5 min de leitura