Voltar para a página

Trinco

Relatório de segurança: DemoAgenda

Versão 1.0 · Análise de 3 a 4 de março · Confidencial

Documento de exemplo. DemoAgenda, seus dados e todos os achados são fictícios. Nenhum sistema real foi testado para criar este relatório.

1. Resumo para decisão

Não recomendamos abrir o app para clientes antes de corrigir os itens crítico e altos. Hoje, uma pessoa sem conta consegue ler dados de clientes, e a chave administrativa do banco está exposta. Os dois problemas juntos permitem copiar ou alterar a base inteira.

A boa notícia: os itens mais graves têm correção objetiva e devem caber em cerca de um dia de trabalho, se a estrutura real for parecida com a observada.

IDGravidadeProblemaCorrigir
RX-01CríticoTabela de agendamentos públicaAgora
RX-02AltoChave administrativa no navegadorAgora
RX-03AltoFatura de outro cliente pode ser abertaAntes de lançar
RX-04AltoWebhook aceita pagamento falsoAntes de cobrar
RX-05MédioLogin sem limite de tentativasAté 7 dias
RX-06MédioAssistente de IA sem limite de usoAté 7 dias
RX-07BaixoCabeçalhos de segurança ausentesAté 30 dias

2. O que foi analisado

AplicativoDemoAgenda, agenda online para pequenos consultórios
Ambientehomolog.demoagenda.example, com dados fictícios
TecnologiasNext.js, Supabase e Stripe, gerados com auxílio de IA
AcessosDuas contas de usuário comum, uma de administrador e leitura do repositório
AutorizaçãoTermo de escopo assinado pelo responsável legal
Fora do escopoSobrecarga, engenharia social, produção e sistemas de terceiros

3. Achados

CríticoRX-01

Tabela de agendamentos pode ser lida sem login

O que isso significa: qualquer visitante pode baixar nomes, telefones e horários dos clientes.

Como reproduzir

curl "https://PROJETO.supabase.co/rest/v1/agendamentos?select=*&limit=1" \
  -H "apikey: CHAVE_PUBLICA_ANON"

Resultado: a API devolveu um registro fictício sem sessão. O teste parou no primeiro registro, como definido no escopo.

Causa

A regra de segurança por linha (RLS) está desligada na tabela. A chave pública é visível no navegador por natureza, então não pode ser a única proteção.

Correção

alter table public.agendamentos enable row level security;

create policy "usuario le os proprios agendamentos"
on public.agendamentos for select
to authenticated
using ((select auth.uid()) = usuario_id);

Repita a revisão nas operações de inclusão, alteração e exclusão, e nas demais tabelas.

Prompt para sua ferramenta de IA

Ative RLS em todas as tabelas públicas do Supabase. Crie políticas para que cada
usuário acesse apenas registros em que usuario_id seja igual a auth.uid(). Liste
cada política e não use "using (true)".
AltoRX-02

Chave administrativa enviada ao navegador

O que isso significa: quem encontrar a chave pode ignorar as regras do banco, ler, alterar ou apagar dados.

Evidência

O arquivo JavaScript público contém o valor de NEXT_PUBLIC_SUPABASE_SERVICE_ROLE. A chave foi identificada, mas não utilizada.

Correção

  1. Gerar uma nova chave no Supabase e revogar a atual.
  2. Renomear a variável, retirando o prefixo NEXT_PUBLIC_.
  3. Usar a chave somente no servidor e publicar novamente.
  4. Revisar registros de acesso em busca de uso desconhecido.
AltoRX-03

Usuário abre fatura de outro cliente ao trocar o ID

O que isso significa: um cliente logado pode ver dados e valores de outro.

Como reproduzir

  1. Entrar com a conta de teste A.
  2. Abrir /api/faturas/1048, que pertence à conta de teste B.
  3. Observar que a API responde 200 com os dados da fatura.

Correção

const fatura = await buscarFatura(id);
if (!fatura || fatura.usuarioId !== sessao.usuarioId) {
  return Response.json({ erro: "Não encontrada" }, { status: 404 });
}
AltoRX-04

Webhook de pagamento aceita evento sem assinatura

O que isso significa: uma pessoa pode tentar ativar um plano pago enviando um aviso falso de pagamento.

Evidência

No ambiente de teste, um evento fictício sem o cabeçalho Stripe-Signature foi aceito e alterou uma conta de teste.

Correção

const corpo = await request.text();
const assinatura = request.headers.get("stripe-signature");
const evento = stripe.webhooks.constructEvent(
  corpo,
  assinatura,
  process.env.STRIPE_WEBHOOK_SECRET
);

Trate falha de validação como erro 400 e não altere a conta.

MédioRX-05

Login sem limite de tentativas

Foram feitas 30 tentativas controladas contra uma conta de teste, sem bloqueio ou atraso.

Correção: aplicar limite por conta e por IP, atraso progressivo e alerta para volume anormal.

MédioRX-06

Assistente de IA sem limite de uso

Uma conta comum pode fazer pedidos sem cota, o que pode gerar custo inesperado.

Correção: definir limite por usuário, limite de tamanho, teto mensal no provedor e alerta de gasto. Não permitir que texto do usuário amplie as permissões do assistente.

BaixoRX-07

Cabeçalhos de segurança ausentes

Não foram encontrados Content-Security-Policy e X-Content-Type-Options.

Correção: configurar os cabeçalhos e testar para não bloquear scripts legítimos.

4. Pontos sem problema encontrado

Isso não prova ausência de falhas. Registra apenas o resultado dos testes feitos.

5. Plano de correção

  1. Hoje: revogar a chave, publicar sem ela, ativar RLS e revisar registros de acesso.
  2. Antes de lançar: corrigir o acesso às faturas.
  3. Antes de cobrar: validar a assinatura do webhook.
  4. Até 7 dias: limitar login e uso do assistente de IA.
  5. Até 30 dias: configurar cabeçalhos e revisar após grandes mudanças.

Possível incidente: como a chave estava pública, avalie se houve acesso indevido. Havendo indício real, procure orientação jurídica sobre as obrigações da LGPD.

6. Limites da análise

Este relatório retrata o ambiente e o período indicados. Ele não garante ausência de vulnerabilidades, não é certificação e não substitui assessoria jurídica. Ataques de sobrecarga, engenharia social, produção e sistemas de terceiros não foram testados.

Gravidade considera o impacto no negócio e a facilidade de exploração neste contexto. Evidências são apagadas 30 dias depois da entrega, salvo acordo escrito diferente.