Ataque de Reentrância: Por que os Contratos Inteligentes continuam a ser drenados (E como parar isso)

A Versão Rápida: Reentrância é como um hacker a ligar-te de volta enquanto ainda estás a transferir dinheiro para ele—eles drenam a tua carteira antes que a transação termine.

Aqui está a dura verdade: Mais de $100M foram perdidos devido a exploits de reentrância. O mais famoso? O hack da DAO (2016) roubou $50M em ETH ao explorar esta vulnerabilidade exata.

Como o Ataque Funciona (Usando Lógica de Código Real)

Imagine que o ContractA possui 10 ETH e o ContractB tem um saldo de 1 ETH armazenado dentro dele.

Quando o ContractB chama withdrawAll(), eis o que deve acontecer:

  1. Verificar saldo > 0 ✓
  2. Enviar ETH de volta ✓
  3. Atualizar saldo para 0 ✓

Mas aqui é onde quebra: O atacante explora a ordem das operações.

O Fluxo de Exploração:

  • O atacante chama attack() → que chama withdrawAll() no ContractA
  • ContractA envia 1 ETH e aciona a função de fallback do atacante ()
  • Antes que o saldo seja atualizado para 0, fallback() chama imediatamente withdrawAll() novamente
  • ContractA verifica: “O saldo > 0?” SIM (porque ainda não foi atualizado!)
  • Envia mais 1 ETH → aciona novamente o fallback()
  • Este ciclo continua até que o ContractA esteja completamente esgotado

Insight chave: A atualização do saldo acontece APÓS a transferência de ETH. Essa é a janela de vulnerabilidade.

Três Estratégias de Defesa

1. O Modificador nonReentrant (Proteção de Função Única)

Bloqueie a função enquanto ela está a ser executada. Não é permitido reentrada: solidity modifier nonReentrant { require(!locked, “No reentrancy”); bloqueado = verdadeiro; _; locked = false; }

Simples, mas protege apenas uma função de cada vez.

2. Padrão de Verificações-Efeitos-Interações (Proteção Multi-Função)

Este é o fator decisivo:

  • Verificações: Verificar condições (balance > 0)
  • Efeitos: Atualizar estado (balance = 0) ← Mover isto ANTES de enviar ETH
  • Interações: Enviar ETH

Ordem errada:

require(balance > 0); → enviar ETH → saldo = 0; // Muito tarde!

Ordem correta:

require(balance > 0); → saldo = 0; // Atualizar PRIMEIRO → enviar ETH // Depois interagir

Agora, mesmo que fallback() reentre, o saldo já é 0. O ataque falha.

3. GlobalReentrancyGuard (Proteção entre Contratos)

Para sistemas complexos com múltiplos contratos interativos, utilize um contrato guardião centralizado que rastreie o estado de bloqueio em todos os contratos. Quando o ContratoA chama o ContratoB, o guardião registra isso—se o ContratoB tentar chamar de volta ao sistema antes de retornar, o guardião bloqueia.

Porque Isso Importa

A reentrância não é apenas um problema do Solidity — é um problema de design. Cada vez que você envia ETH ou chama funções externas, está entregando o controle a código não confiável. A função de fallback do atacante é executada no CONTEXTO do SEU contrato.

Os dados: A Chainalysis encontrou que ~60% dos exploits de alto valor em 2023-2024 envolveram reentrância ou padrões semelhantes. Projetos de topo como Yearn, Curve e Balancer tiveram todos sustos de reentrância.

Conclusão: Use Checks-Effects-Interactions por padrão. Adicione nonReentrant onde necessário. Para sistemas de múltiplos contratos, implemente GlobalReentrancyGuard. Os $100M+ perdidos poderiam ter sido salvos com esses padrões básicos.

Siga @TheBlockChainer para mais mergulhos profundos em segurança Web3.

ETH2.23%
CRV1.62%
BAL2.6%
Ver original
Esta página pode conter conteúdo de terceiros, que é fornecido apenas para fins informativos (não para representações/garantias) e não deve ser considerada como um endosso de suas opiniões pela Gate nem como aconselhamento financeiro ou profissional. Consulte a Isenção de responsabilidade para obter detalhes.
  • Recompensa
  • Comentário
  • Repostar
  • Compartilhar
Comentário
0/400
Sem comentários
  • Marcar
Negocie criptomoedas a qualquer hora e em qualquer lugar
qrCode
Escaneie o código para baixar o app da Gate
Comunidade
Português (Brasil)
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)