O contexto
O sistema em questão é uma plataforma real, em produção, que processa pagamento e coleta dado de cliente para gerar documentos técnicos. Ele foi construído com apoio de ferramentas de IA e passou por várias rodadas de revisão ao longo dos meses — mas, como em qualquer sistema, sempre existe algo que ninguém pensou em verificar.
Antes de vender auditoria de segurança para outras empresas, decidimos aplicar o mesmo processo nos nossos próprios sistemas. Essa decisão rendeu um achado que resume bem por que esse tipo de revisão importa: não encontramos um ataque sofisticado. Encontramos uma trava simples que faltou.
O que estava errado
O sistema tinha uma rota de API que enviava um e-mail de confirmação sempre que um cliente terminava de preencher um formulário. Essa rota exigia login — ou seja, só um usuário autenticado podia chamá-la. Isso parece seguro à primeira vista, e é exatamente por isso que esse tipo de falha passa despercebida.
O problema é que a rota não tinha nenhum limite de quantas vezes o mesmo usuário podia chamá-la. Um cliente comum, usando a própria conta paga (sem precisar de senha roubada nem de nenhuma técnica avançada), conseguia chamar essa rota centenas de vezes seguidas.
Cada chamada disparava um e-mail de verdade através do provedor de envio do sistema. Provedores como esse costumam ter um limite mensal de e-mails — no caso, a cota era de alguns milhares por mês. Um único usuário, sozinho, conseguiria esgotar essa cota inteira em minutos.
O resultado não seria "essa pessoa recebeu muito e-mail". O resultado seria: ninguém mais recebe e-mail nenhum, incluindo os clientes que pagaram naquele mesmo dia e estavam esperando a confirmação do pagamento.
Por que isso é mais grave do que parece
Esse tipo de falha se chama exaustão de recurso (resource exhaustion): em vez de invadir o sistema, o atacante simplesmente consome um recurso limitado (nesse caso, a cota de envio de e-mail) até que ele acabe para todo mundo. É uma categoria de ataque que não precisa de conhecimento técnico avançado — só precisa que ninguém tenha colocado uma trava.
No nosso caso específico, a falha não permitia acessar dados de outra pessoa. Mas o efeito prático seria real: e-mails de confirmação de pagamento, avisos de documento pronto e alertas de vencimento parariam de sair para todos os clientes ativos, não só para quem causou o problema.
Como corrigimos
A solução não exigiu nenhuma ferramenta cara nem infraestrutura nova. Adicionamos uma coluna no banco de dados que registra o momento exato em que o e-mail de confirmação foi enviado para cada pedido. A rota passou a fazer uma verificação simples antes de enviar qualquer coisa:
- Se o pedido ainda não tinha e-mail registrado, o sistema envia e marca a data.
- Se o pedido já tinha e-mail registrado, o sistema responde normalmente, mas não envia de novo.
Essa verificação é feita de forma atômica no banco (uma única operação de "atualizar somente se ainda estiver vazio"), o que evita até mesmo a situação de duas chamadas simultâneas conseguirem passar pela verificação ao mesmo tempo.
Testamos a correção chamando a rota três vezes seguidas com a mesma conta: a primeira chamada enviou o e-mail normalmente, a segunda e a terceira retornaram confirmando que o e-mail já tinha sido enviado, sem disparar nada de novo.
O que fizemos questão de não fazer
Para confirmar que a falha era real, seria tentador simplesmente automatizar centenas de chamadas contra o sistema em produção e ver a cota de e-mail esgotar de verdade. Decidimos não fazer isso.
Um teste desse tipo teria o mesmo efeito colateral do próprio problema que estávamos tentando resolver: gastaria cota real, arriscaria a reputação do domínio de e-mail perante os provedores (Gmail, Outlook e outros analisam padrão de envio para decidir o que é spam), e traria exatamente o risco que a correção existia para evitar.
Em vez disso, a auditoria foi feita por revisão de código primeiro (identificando a ausência da trava antes mesmo de testar), e a validação em produção foi limitada a três chamadas controladas — o suficiente para provar que a correção funciona, sem reproduzir o dano.
Essa é uma diferença importante entre uma auditoria séria e um teste irresponsável: provar o risco não exige causar o dano de verdade.
A lição que fica
Essa falha não apareceu em nenhum teste manual do sistema. Um cliente comum preenchendo o formulário normalmente nunca chamaria a rota duas vezes — a falha só existe para quem procura especificamente por esse tipo de brecha, ou para quem tem uma auditoria olhando por dentro do código antes que isso aconteça.
É exatamente esse tipo de achado — invisível no uso normal, óbvio depois que alguém aponta — que uma auditoria de segurança existe para encontrar antes que vire um problema real.