O teste das duas contas: abra duas, tente ver o dado da outra, e descubra em 10 minutos se seu app vaza
Trocar o número no endereço pega um vazamento. Faltam outros cinco. O roteiro completo com duas contas, uma folha de papel e dez minutos.
"IDOR básico: usuário A acessa dados do usuário B só mudando um ID na URL."
Está numa lista de falhas comuns em app feito com IA que circulou entre programadores brasileiros. IDOR é o nome técnico; a tradução é "trocar um número no endereço e ver o dado de outra pessoa". A frase é certa e, pra quem não é da área, não serve pra nada. Na mesma discussão sobre segurança, apareceu outra: "quem nunca estudou isso não sabe o que não sabe".
Hoje é sobre transformar a primeira frase em coisa que você faz, e a segunda em coisa que você passa a saber.
Já publiquei a versão de cinco minutos: crie duas contas, copie o endereço de um pedido da conta A, cole logado como B. Ela pega o caso clássico, o número no endereço. Só que o dado de um cliente tem pelo menos mais cinco caminhos pra aparecer na tela de outro, e nenhum deles passa pelo endereço. Este post é o roteiro completo: duas contas, uma folha de papel, dez minutos.
Por que a IA não pega isso sozinha
Um programador contratado pra consertar um sistema feito no vibe coding (o jeito de construir pedindo pra IA, sem escrever código) descreveu assim o que encontrou:
"Sistema que só funcionavam usando uma conta específica. Qualquer outra conta retornava erro."
Repare no avesso disso. O sistema foi testado com uma conta. A sua também, aposto. Você cria a conta, cria um pedido, vê o pedido, fica feliz. Com uma conta só, todo dado que aparece na tela é seu mesmo, e a pergunta "esse dado é de quem está olhando?" nunca aparece.
A IA testa igual. Ela confere o caminho feliz: login funcionou, pedido salvou, lista mostrou. Não é preguiça dela nem burrice sua. É que a falha só existe quando tem duas pessoas, e ninguém pôs a segunda pessoa no teste.
O caso que virou registro oficial
Em março de 2025, o engenheiro Matt Palmer achou essa falha num app feito no Lovable. Escreveu um programa que visitou 1.645 apps da vitrine pública da plataforma e encontrou 170 (cerca de 10%) com 303 pontos de acesso devolvendo dado que não deviam: e-mail, nome, telefone, assinatura, pagamento. A falha virou registro público de vulnerabilidade, o CVE-2025-48757, publicado em 29 de maio de 2025.
O que esse número não prova: não é a taxa de falha de todo app feito com IA, é a de uma vitrine, num mês de 2025. E Palmer só olhou a página inicial, sem fazer login. Ele mesmo escreveu que a parte logada provavelmente expunha mais, e não mediu.
O detalhe que me interessa é outro. O verificador de segurança que o Lovable lançou em seguida, segundo Palmer, "só checa se existe alguma regra, não se ela está correta". Ter regra e ter a regra certa são coisas diferentes. Nenhum botão de "verificar segurança" sabe o que é certo pro seu negócio. O teste das duas contas sabe, porque ele não lê a regra: ele tenta passar por ela.
Antes de começar: o preparo (2 minutos)
- Dois navegadores. Um normal e uma janela anônima (Ctrl+Shift+N no Chrome, Cmd+Shift+N no Mac). Cada um guarda um login diferente.
- Conta A e conta B, com e-mails diferentes. Use dois e-mails seus.
- Uma marca. Logado como A, crie um item (pedido, cliente, anotação, o que o seu app guarda) e escreva num campo de texto uma palavra que não existe em mais lugar nenhum:
ZEBRA77. Se o app aceita anexo (foto, PDF), anexe um. - Uma folha de papel com seis linhas numeradas. Cada linha vai ganhar um ✓ ou um ✗.
Faça isso no app publicado, com dados de teste. Nunca no app de outra pessoa: o teste é no que é seu.
Os seis testes
1. O endereço (1 minuto)
O da versão curta. Como A, abra o item e copie o endereço da barra, algo como meuapp.com.br/pedido/1043. Como B, cole. Tem que dar "não encontrado" ou "sem permissão". Se o pedido de A aparecer, ✗.
2. A lista e a busca (1 minuto)
Como B, abra toda tela que mostra lista: pedidos, clientes, painel, histórico. Use a busca do app e procure ZEBRA77.
B não pode achar nada. E repare que o teste 1 não pega esse caso: o endereço está protegido, mas a lista puxa "todos os pedidos" e não "os pedidos de quem está logado".
3. Editar e apagar (2 minutos)
Ver é ruim. Mudar é pior. Como A, abra o item e clique em editar; se o endereço mudar (/pedido/1043/editar), copie. Como B, cole e tente salvar uma alteração.
Volte pra janela de A e recarregue. O item mudou? ✗. Se o app tiver botão de apagar, repita com um segundo item de teste de A. Só com dado de teste, claro.
4. O anexo (1 minuto)
Como A, abra a foto ou o PDF que você anexou, clique com o botão direito, "copiar endereço da imagem" (ou do link). Cole numa terceira janela anônima, sem login nenhum.
Abriu? ✗. Aqui não precisa nem ser cliente: qualquer um com o link vê. É comum porque, no Supabase (o banco que fica por trás do Lovable e de muita outra ferramenta), arquivo vive num "balde", e a própria documentação diz que num balde público "qualquer um que tenha o endereço do arquivo consegue acessá-lo", e que isso "contorna os controles de acesso" pra baixar. A regra que você escreveu pro banco não vale pro arquivo. Documento, comprovante e foto de cliente não podem morar em balde público.
5. O que chega no navegador sem aparecer na tela (2 minutos)
Este é o que quase ninguém faz, e é o que mais me preocupa.
A tela mostra o que o app escolhe mostrar. Mas o que o seu navegador recebeu pode ser mais. Tem app que pede ao banco "todos os pedidos" e só depois filtra na tela os do usuário. Visualmente, perfeito. Por baixo, o pedido de A chegou no computador de B.
Pra ver, como B: aperte F12 (no Mac, Cmd+Option+I). Abre o painel de ferramentas do navegador. Clique na aba Rede (Network, se estiver em inglês). Recarregue a tela de lista. Com o painel aberto, aperte Ctrl+F (Cmd+F no Mac) e procure ZEBRA77.
Essa busca olha tudo que o servidor mandou, não só o que apareceu. Achou? ✗. O dado de A está saindo pra B, só não está sendo exibido. Você não invadiu nada: olhou o que o seu próprio app entregou no seu próprio navegador.
6. O que sai do app (1 minuto)
Exportar planilha, gerar relatório, PDF de resumo, e-mail automático. Se existir, rode como B e procure ZEBRA77 no arquivo. Exportação costuma ser escrita depois, às pressas, e a regra de quem-vê-o-quê fica pra trás.
Leia a folha
Seis ✓: o seu app separa um cliente do outro nesses seis caminhos. Não quer dizer que ele está seguro em tudo. Quer dizer que a falha mais comum não está nele hoje.
Algum ✗? Agora você tem uma coisa que não tinha antes: o problema com nome e endereço. E o pedido pra IA tem que ter os dois. "Melhora a segurança" não serve. Assim serve:
| Falhou | O pedido |
|---|---|
| 1 ou 3 | "No servidor, antes de mostrar, alterar ou apagar um pedido, confira se ele pertence ao usuário logado. Se não pertencer, responda com erro de permissão." |
| 2 ou 6 | "A lista, a busca e a exportação de pedidos devem trazer só as linhas cujo dono é o usuário logado. A regra fica no banco, não na tela." |
| 4 | "Mova os anexos de clientes pra um balde privado e gere links temporários só pro dono do arquivo." |
| 5 | "A consulta da tela de lista está trazendo pedidos de outros usuários e filtrando na tela. Faça o filtro no banco, com regra de acesso por linha." |
Um pedido de cada vez. Depois de cada um, refaça o teste daquela linha, você, nas duas janelas. "Pronto, corrigido" não é ✓. ✓ é você olhando a janela de B e não vendo a zebra.
Se o seu app usa Supabase, uma coisa da semana: o Supabase está mudando um padrão. Tabela nova deixou de nascer acessível pela internet em projetos novos desde 30 de maio, e a mudança chega aos projetos antigos em 30 de outubro de 2026. Bom. Mas o próprio aviso diz que "o comportamento do RLS continua o mesmo". RLS é a regra por linha do banco, a que diz "cada linha tem dono e só o dono lê". Ou seja: a regra que decide qual cliente vê qual linha continua sendo a que você escreve. E quando a tabela nova der erro de permissão, a tentação da IA vai ser liberar geral pra "funcionar". Depois de qualquer correção desse tipo, o teste das duas contas de novo.
Quando refazer
A folha não vai pro lixo. Refaça os seis testes sempre que:
- a IA mexer em login, cadastro ou banco de dados;
- aparecer tela nova com lista, busca ou exportação;
- você trocar de plataforma ou de banco.
Dez minutos. Menos que um café com um cliente explicando por que ele viu o pedido do outro.
O que isso era, na parede inteira
Este é um teste da fase que eu chamo de Estruture: o portão dela pede regras testadas e login que protege, e o teste das duas contas é como você prova a segunda coisa sem ler uma linha de código. Mas ele vem depois de outras: o app precisa estar num banco que é seu, com teste separado do de verdade, e com um ponto de retorno se a correção quebrar outra coisa. E tem vizinhos que ele não cobre: a área de admin que só esconde o botão, a chave secreta que foi parar no navegador, o app inteiro aberto pra quem nem fez login.
Uma folha com seis linhas resolve uma parede. O app tem várias, e elas têm ordem.
Fontes
- Matt Palmer, Statement on CVE-2025-48757 (1.645 apps, 170 vulneráveis, 303 pontos de acesso; só página inicial, sem login; verificador que "checa existência, não correção"): https://mattpalmer.io/posts/statement-on-CVE-2025-48757/
- NVD, registro CVE-2025-48757: https://nvd.nist.gov/vuln/detail/CVE-2025-48757
- Supabase, Storage buckets (balde público: "anyone who possesses the asset URL can readily access the file"): https://supabase.com/docs/guides/storage/buckets/fundamentals
- Supabase Changelog, tabelas novas deixam de ser expostas automaticamente (28/04/2026, 30/05/2026, 30/10/2026; "RLS behavior remains unchanged"): https://supabase.com/changelog/45329-breaking-change-tables-not-exposed-to-data-and-graphql-api-automatically