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:
Verificar saldo > 0 ✓
Enviar ETH de volta ✓
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.
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.
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:
Mas aqui é onde quebra: O atacante explora a ordem das operações.
O Fluxo de Exploração:
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:
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.