Falhas de segurança que não pode ignorar

Como evitar deixar a porta das traseiras do seu código escancarada

Walter Gandarella • 27 de fevereiro de 2025

Sabe qual é o maior problema de segurança da sua aplicação ou SaaS? Não, não é aquele exploit sofisticado que viste no último evento de segurança, nem um qualquer ataque de rede super complexo. A maior ameaça está literalmente «entre a cadeira e o monitor». Sim, estou a falar de si, developer, que está a deixar a porta dos fundos escancarada sem sequer se aperceber.

O problema não é que o seu código possa ser pirateado. O problema é que, em muitos casos, já o deixou praticamente aberto. E o pior? Muitas destas vulnerabilidades são tão básicas que deixá-las ali depois de ler este artigo seria quase uma sabotagem.

Não me vou concentrar em grandes CVEs ou vulnerabilidades de rede. Afinal, provavelmente está a alojar na Vercel, Google, Amazon ou outra empresa com camadas decentes de proteção. Provavelmente está a utilizar um ORM que dificulta a injeção de SQL e a construir interfaces com JSX que reduz riscos de XSS. Isto não significa que estes ataques sejam impossíveis, mas existem centenas de artigos sobre estas vulnerabilidades por aí. Vamos focar-nos no que realmente está a deixar a sua aplicação vulnerável no dia a dia.

Webhooks: A porta das traseiras desprotegida

Toda a gente adora webhooks, certo? É uma forma prática de integrar sistemas. Mas já reparou que quase todos usam a mesma rota padrão? /api/webhook ou /api/hook. Isto por si só não é um problema, mas torna-se um quando qualquer pessoa mal-intencionada pode facilmente adivinhá-la e, pior ainda, enviar-lhe dados falsos.

Imagine um utilizador a enviar um pedido para a sua rota de webhook fingindo ser o seu gateway de pagamento, confirmando uma compra que nunca aconteceu. Assustador, não?

A solução é simples: sempre que configurar um webhook, utilize uma assinatura secreta. No Stripe, recebe o cabeçalho stripe-signature, enquanto no Mercado Pago (do Brasil) existe o x-signature. O seu backend deve validar este código para garantir que o pedido veio da fonte correta. Quando um utilizador malicioso tenta contornar o sistema, a sua API vai simplesmente queixar-se da falta de assinatura.

IDOR: Quando a sua aplicação entrega dados de outros utilizadores

IDOR (Insecure Direct Object Reference) é aquela falha em que não se verificam permissões quando se acede a objetos via API. É mais comum do que se imagina e causa arrepios só de pensar nas consequências.

Imagine que tem um endpoint /api/purchase/:id onde os utilizadores acedem aos detalhes das suas compras. O código é simples: o utilizador faz um pedido GET para /api/purchase/123 e tu devolves os detalhes. Mas e se a compra do ID 123 não for dele? Parabéns, acabou de expor dados de outro cliente!

Outro erro clássico é ter um endpoint PATCH /api/profile onde o servidor recebe o ID do utilizador no corpo do pedido. Nunca faça isso! O ID do utilizador deve sempre provir da sessão, do JWT ou de outro mecanismo de autenticação, nunca do corpo do pedido que possa ser facilmente manipulado.

A regra é clara: valide sempre quem está a pedir acesso antes de mostrar, editar ou apagar o que quer que seja.

Exposição de dados: Menos é mais

Este problema é mais subtil, mas igualmente devastador. Digamos que criou um marketplace onde é possível aceder aos detalhes de um produto através do pedido GET com o ID do produto. A API devolve os detalhes do produto e algumas informações do vendedor. Até aqui, tudo bem.

Mas e se, juntamente com o nome e a fotografia do vendedor, acabar também por enviar o email, o número de contribuinte, o telefone, a morada e até a palavra-passe encriptada? Isto acontece quando pesquisa o produto, traz todos os dados do vendedor relacionado, e esquece-se de filtrar o que deve ou não ser enviado para o frontend.

A solução é: enviar apenas o que é realmente necessário. Se o frontend apenas necessita do nome e da foto do vendedor, envie apenas isso. Não caia na armadilha de pensar «ah, o frontend só vai usar o que precisa». Proteja os seus utilizadores limitando os dados expostos desde o início.

Rate Limit: Proteger-se da quantidade

Não implementar limites de requisição pode não parecer uma vulnerabilidade direta, mas é certamente uma das coisas que mais prejudica um produto. Imagine que a sua aplicação tem uma API pública para criar posts. Sem limitação, um atacante pode automatizar este pedido e criar milhares de posts falsos em questão de segundos.

Isto não só polui a sua base de dados e degrada a experiência dos utilizadores, como também pode custar caro - literalmente. O armazenamento em base de dados não é gratuito, e sem proteção, acaba por pagar por dados lixo.

Outro exemplo: uma API para enviar emails. Um atacante poderia facilmente estourar o seu limite de envio, forçando-o a pagar taxas adicionais para continuar a operar normalmente.

Nas páginas de login, a ausência de rate limit permite ataques de força bruta para descobrir passwords. Implemente CAPTCHAs, limitadores de tentativas ou outros mecanismos de proteção em endpoints sensíveis ou que impliquem custos.

Mass Assignment: Quando se torna admin sem querer

Esta vulnerabilidade é uma das minhas favoritas porque é tão simples e tão perigosa ao mesmo tempo. Quando tem uma rota PATCH para alterar algumas informações de um objeto, como o nome de utilizador ou a descrição de um produto, esta falha permite alterar qualquer propriedade do objeto.

Além de alterar o seu nome de utilizador, conseguiria alterar o seu cargo para administrador ou qualquer outra propriedade do sistema. A solução? Defina explicitamente quais os campos que podem ser alterados. Não deixe isto aberto.

TOCTTOU: A vulnerabilidade mais elegante

Time of Check to Time of Use (TOCTTOU) é uma vulnerabilidade fascinante que ocorre quando existe um intervalo entre a verificação de uma condição e a utilização do resultado dessa verificação.

O exemplo clássico é de uma operação bancária. Tem 100€ na sua conta e solicita um levantamento desse valor. O programa verifica se tem o dinheiro e, caso tenha, realiza o levantamento. Mas e se enviar duas requisições em simultâneo?

Na prática, é difícil enviar exatamente duas requisições ao mesmo tempo, pelo que geralmente se enviam várias. Devido ao delay de rede, várias instâncias da função serão executadas em paralelo, todas elas passando pela verificação de saldo e depois realizando o levantamento várias vezes.

Isto funciona para muitos cenários: likes em publicações, compra de bilhetes limitados e várias outras operações que dependem de recursos finitos.

A solução é relativamente simples: é necessário «travar» os recursos críticos enquanto estão a ser utilizados. Pode usar semáforos, sistemas de filas, mas o mais comum são as transactions em bases de dados, onde a verificação e a ação acontecem de forma atómica - ou a operação acontece por completo, ou não acontece nada, sem interrupções.

Confiar no frontend: O erro fundamental

Este é talvez o erro mais básico e também o mais comum: acreditar que as validações no frontend são suficientes para garantir a segurança da sua aplicação.

Muita gente pensa: «Se o botão está desativado, o pedido nunca será enviado» ou «Se o frontend mostra que o utilizador tem 10€, só pode levantar 10€, afinal foi o meu servidor que informou esse valor». Não é bem assim.

Qualquer regra implementada no frontend pode ser ignorada ou manipulada pelo utilizador. Isto é especialmente verdade em aplicações que utilizam Client-Side Rendering (CSR).

Para ilustrar: imagine uma aplicação de finanças em que o botão de levantamento fica desativado quando não tem saldo. Um utilizador mal-intencionado pode facilmente inspecionar o código, encontrar a renderização condicional que controla esta visualização, alterar as variáveis ​​relevantes no runtime e ativar o botão.

Claro que, se o backend estiver bem implementado, o pedido de levantamento falhará. Mas nem sempre é assim. Pense num e-commerce que calcula os preços no frontend e envia esse valor para o backend. Se alguém intercetar a requisição e alterar o preço de 500€ para 50€, e o seu backend confiar cegamente nessa informação, terá um problema grave.

A regra é clara: recalcule sempre os preços e verifique todas as condições no servidor. Nunca, mas mesmo nunca, confie no frontend para segurança. Se o seu sistema depende apenas do frontend para proteção, alguém o irá contornar.

A sua app está 100% segura?

Não. E nunca estará.

A segurança a 100% não existe nem nunca existirá. Pense: cada programa que usa para criar ou alojar a sua aplicação foi feito por seres humanos, e os humanos erram. Pode ser um bug no seu código, na framework que utiliza, na base de dados, no sistema operativo… Tudo foi construído por pessoas que, a dada altura, podem ter cometido um único erro capaz de comprometer todo o sistema.

Mesmo que o seu código fosse perfeito (o que é praticamente impossível), nada impediria que alguém invadisse fisicamente o seu servidor, ou que algum funcionário do seu fornecedor de alojamento roubasse as suas credenciais, ou que caísse num phishing sofisticado.

A segurança é um jogo infinito. O objetivo não é estar 100% seguro, mas sim dificultar ao máximo o ataque e reduzir os danos caso algo corra mal. É um processo contínuo de melhoria, vigilância e adaptação.

Por falar nisso, nós na Yes Marketing já saímos na frente neste jogo! Implementámos medidas para mitigar todas estas vulnerabilidades nos nossos sistemas. A nossa equipa não só corrigiu as brechas mais comuns, como também montou um processo de monitorização contínuo para identificar possíveis novas falhas. Dormimos mais descansados ​​sabendo que não estamos apenas a reagir a problemas, mas a procurar ativamente formas de melhorar a nossa segurança antes que qualquer incidente aconteça. É como dizemos por aqui: mais vale prevenir do que ter de explicar ao chefe porque é que o sistema foi pirateado, certo?

Talvez tenha reconhecido algumas dessas falhas nos seus próprios projetos. Não desespere - identificar o problema é o primeiro passo para o resolver. A segurança não tem de ser complicada, mas tem de ser levada a sério.

E lembre-se: se depois de ler tudo isto continuar a cometer estes erros, já não é descuido. É sabotagem.


Últimos artigos relacionados