Attacco Reentrancy in Solidity: come prevenirlo efficacemente

Article author

Gli attacchi di reentrancy rimangono una delle minacce più famose e persistenti per la sicurezza degli smart contract su Ethereum e sulle catene compatibili con EVM. Nonostante anni di sforzi nel settore, nuovi protocolli continuano a essere vittime di vulnerabilità da reentrancy, causando perdite finanziarie significative — talvolta dell’ordine di milioni di dollari. Questi exploit si manifestano spesso in contratti DeFi di lending, staking e bridge, dove le chiamate esterne e i trasferimenti di token sono comuni.

In questo articolo, analizziamo la meccanica di un attacco di reentrancy, identifichiamo i tipici schemi contrattuali vulnerabili e condividiamo strategie di mitigazione collaudate. Basandoci sull’analisi di oltre 282 audit di smart contract condotti da Soken, evidenziamo come pratiche di codifica in Solidity raffinate e workflow di testing completi possano impedire minacce di reentrancy. Questo articolo collega inoltre rischi correlati come l’uso improprio di delegatecall e tattiche di flash loan per fornire una comprensione a tutto tondo, essenziale per ogni sviluppatore Web3 o auditor della sicurezza.


Cos’è un attacco di Reentrancy e come sfrutta gli Smart Contract?

Un attacco di reentrancy avviene quando un contratto malevolo chiama ripetutamente una funzione di un contratto vulnerabile prima che l’esecuzione iniziale sia conclusa, sfruttando uno stato inconsistente del contratto.

In pratica, una vulnerabilità di reentrancy nasce da un ordinamento scorretto delle modifiche di stato e delle chiamate esterne all’interno di un contratto. Quando il contratto trasferisce ETH o token tramite una call o transfer a un indirizzo non attendibile, il destinatario può rientrare ricorsivamente nella funzione vulnerabile prima che le variabili di stato vengano aggiornate. Questo consente all’attaccante di prosciugare fondi o manipolare la logica del contratto in modo imprevisto.

Ad esempio, il celebre hack del DAO nel 2016 ha portato all’esploit di quasi 60 milioni di dollari tramite vulnerabilità di reentrancy, un monito diventato iconico. Da allora, l’industria ha identificato che la causa principale è l’aggiornamento del bilancio o dello stato del contratto solo dopo le chiamate esterne, creando una finestra di stato incoerente sfruttata dall’attaccante.

Insight esperto dagli audit Soken: “In oltre il 40% dei nostri recenti audit di sicurezza DeFi, schemi di reentrancy emergono in qualche forma, spesso con varianti sottili che coinvolgono contratti proxy o chiamate a strati. Identificare questi richiede un’analisi approfondita delle chiamate inter-contratto, oltre la semplice ispezione di un singolo contratto.”


Schemi comuni in Solidity che causano vulnerabilità di Reentrancy

Le vulnerabilità di reentrancy tipicamente derivano da una specifica sequenza di operazioni: chiamate esterne (come trasferimenti di ETH o chiamate token) che avvengono prima che il contratto aggiorni le sue variabili di stato interne.

Lo schema più vulnerabile è il seguente:

function withdraw(uint256 amount) public {
    require(balances[msg.sender] >= amount);
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success);
    balances[msg.sender] -= amount;
}

Qui la chiamata esterna con call.value() avviene prima della detrazione del saldo dell’utente. Durante quella chiamata esterna, la funzione fallback di un attaccante può invocare ricorsivamente withdraw(), prosciugando fondi prima che i saldi vengano aggiornati.

Costrutti chiave a rischio includono:

  • Chiamate esterne prima degli aggiornamenti di stato: invio di ETH o token prima di aggiornare i bilanci.
  • Uso di call o send senza precauzioni: chiamate esterne tramite funzioni di basso livello aggirano i controlli e possono invocare fallback functions.
  • Mancanza di guardian contro la reentrancy: assenza di mutex o altri meccanismi di blocco.
  • Catene di chiamate complesse con delegatecall: possono indirettamente aprire a reentrancy tramite chiamate esterne in contratti chiamati.

Al contrario, lo schema più sicuro inverte l’ordine:

function withdraw(uint256 amount) public {
    require(balances[msg.sender] >= amount);
    balances[msg.sender] -= amount;  // aggiornamento stato prima
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success);
}

Come prevenire attacchi di Reentrancy in Solidity: best practice e strumenti

La difesa più efficace contro la reentrancy è aggiornare lo stato prima delle chiamate esterne combinato con pattern espliciti di protezione da reentrancy.

Tecniche standard di mitigazione:

  1. Pattern Checks-Effects-Interactions:
    Aggiornare sempre lo stato interno prima di effettuare chiamate esterne. Ciò riduce lo stato incoerente del contratto durante le chiamate esterne.

  2. Reentrancy Guards / Mutex:
    Il ReentrancyGuard di OpenZeppelin usa un semplice mutex per bloccare chiamate nidificate. Esempio d’uso:

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract SecureContract is ReentrancyGuard {
    mapping(address => uint256) balances;

    function withdraw(uint256 amount) public nonReentrant {
        require(balances[msg.sender] >= amount);
        balances[msg.sender] -= amount;
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success);
    }
}
  1. Limitare le Chiamate Esterne:
    Minimizzare trasferimenti o interazioni all’interno di funzioni critiche — preferire pagamenti pull anziché push, con utenti che ritirano esplicitamente fondi.

  2. Analisi Statica e Strumenti Automatici:
    Strumenti come Slither e i framework di audit Soken segnalano la reentrancy.

  3. Usare le Versioni Recenti di Solidity e le loro Funzionalità:
    Solidity 0.8+ introduce controlli built-in sugli overflow, ma i rischi di reentrancy persistono, quindi è necessario combinare la sicurezza del codice con pattern progettuali.

Metodo Soken: I nostri audit integrano modellazione manuale delle minacce, esecuzione simbolica e fuzz testing, rilevando flussi complessi di reentrancy spesso non individuati da scanner generici.


Esempi reali di exploit di Reentrancy e il loro impatto

Gli attacchi di reentrancy restano frequenti nonostante la maggiore consapevolezza. Di seguito una comparazione di hack rilevanti che mostrano la diversità e il costo di questi exploit:

Incidente Data Ammontare Perdite Schema Sfruttato Lezione Chiave
The DAO Hack 2016-06 ~$60 milioni Reentrancy nel splitDAO Aggiornare stato prima chiamata
The Lendf.Me Hack 2020-04 $25 milioni+ Reentrancy amplificata da flash loan Combinare guardia reentrancy + resilienza flash loan
Euler Finance Hack 2023-03 $197 milioni Reentrancy nidificata + delegatecall Catene di chiamate complesse aumentano il rischio
Recent DeFi Bridge Hack 2025-11 $15 milioni Exploit di reentrancy su trasferimento token Audit delle interazioni cross-contract

Nota: Gli audit Soken enfatizzano la sicurezza a livelli multipli, soprattutto riguardo chiamate cross-contract e standard token che possono attivare fallback dinamicamente.


Delegatecall e vettori flash loan: amplificare i rischi di Reentrancy

Delegatecall, che esegue codice nel contesto del contratto chiamante, può oscurare il rischio di reentrancy e ampliare la superficie di attacco, soprattutto se combinato con flash loan.

Gli attacchi di reentrancy spesso sfruttano flash loan per massimizzare il capitale disponibile per prosciugamenti ricorsivi:

  • I flash loan forniscono grossa liquidità immediata in una singola transazione.
  • Un attaccante usa fondi presi in prestito per innescare chiamate reentrant per prosciugare la liquidità del protocollo prima di restituire il prestito.
  • L’uso di delegatecall può involontariamente attivare codice esterno non attendibile che modifica lo stato del contratto o permette reentrancy.

Insight di sicurezza: “Nell’esperienza Soken, la mitigazione della reentrancy va oltre i singoli contratti a una progettazione a livello di protocollo — specialmente quando delegatecall e flash loan coesistono. I contratti devono combinare guardie di reentrancy con validazione degli ingressi e controlli specifici per flash loan (limiti sull’ammontare, restrizioni sulle chiamate ricorsive).”


Tabella comparativa: tecniche comuni per prevenire la Reentrancy

Tecnica Descrizione Vantaggi Svantaggi Uso consigliato
Checks-Effects-Interactions Aggiornare stato prima di chiamate esterne Semplice, efficace Richiede disciplina Pratica di sicurezza di base
Reentrancy Guard (mutex) Usare modifier Solidity per impedire reentrancy Blocca esplicitamente chiamate ricorsive Aggiunge un leggero overhead di gas Raccomandato per tutte le funzioni di prelievo
Pull Payment Pattern Far ritirare i fondi volontariamente agli utenti Minimizza i rischi di trasferimenti automatici Richiede interazione utente Ideale per contratti di escrow e staking
Analisi Statica e Dinamica Audit automatici e manuali del codice Individua vulnerabilità precocemente Può non rilevare flussi incrociati complessi Essenziale per sicurezza pre-distribuzione
Controlli Flash Loan & Delegatecall Validare chiamante e contesto transazioni Previene reentrancy amplificata dai prestiti Complesso da implementare Critico in protocolli DeFi con prestiti

Esempio di codice Solidity: contratto vulnerabile alla Reentrancy e sua correzione

Ecco un contratto minimo vulnerabile e la sua versione rinforzata con un reentrancy guard:

// Vulnerabile: prelievo prima dell'aggiornamento del saldo
contract Vulnerable {
    mapping(address => uint256) public balances;

    function deposit() external payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw(uint256 amount) external {
        require(balances[msg.sender] >= amount, "Insufficient funds");
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
        balances[msg.sender] -= amount; // Vulnerabile: stato aggiornato dopo la chiamata esterna
    }
}

Corretto con ReentrancyGuard di OpenZeppelin e pattern effetti prima interazione:

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract Secure is ReentrancyGuard {
    mapping(address => uint256) public balances;

    function deposit() external payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw(uint256 amount) external nonReentrant {
        require(balances[msg.sender] >= amount, "Insufficient funds");
        balances[msg.sender] -= amount; // Stato aggiornato prima della chiamata esterna
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
    }
}

Consiglio professionale: Combinate sempre il pattern checks-effects-interactions con guardie di reentrancy. Audit approfonditi dei grafi di chiamata e test con strumenti di fuzzing possono svelare vulnerabilità di reentrancy multi-hop prima del deploy.


Le vulnerabilità di reentrancy restano una minaccia critica per gli smart contract, specialmente negli ecosistemi DeFi complessi che utilizzano flash loan e delegatecall. Le difese più efficaci combinano disciplina nel codice (checks-effects-interactions), guardie esplicite contro reentrancy, audit rigorosi e validazioni contestuali delle transazioni.

In Soken, la nostra ricerca sulla sicurezza integra tecniche manuali e automatiche affinate da oltre 282 audit di smart contract, identificando e correggendo costantemente reentrancy e rischi associati. Applicare best practice e metodologie di testing approfondite è imprescindibile per ogni progetto che gestisce fondi degli utenti.


Hai bisogno di consulenza esperta sulla sicurezza? Il team di auditor Soken ha revisionato oltre 255 smart contract assicurando più di 2 miliardi di dollari in valore di protocollo. Che tu necessiti di un audit completo, di una valutazione di sicurezza X-Ray gratuita o di supporto nella navigazione delle regolamentazioni crypto, siamo pronti ad aiutarti.

Parla con un esperto Soken | Visualizza i nostri report di audit

Article author

Domande frequenti

Cos'è un attacco reentrancy nei smart contract?

Un attacco reentrancy avviene quando un contratto malevolo chiama ripetutamente il contratto vittima prima che la prima chiamata si completi, sfruttando chiamate esterne per drenare asset o modificare lo stato in modo imprevisto.

Quali pattern di smart contract sono più vulnerabili al reentrancy?

I contratti che eseguono chiamate esterne, come trasferimenti di token o chiamate ad altri contratti, prima di aggiornare lo stato interno sono altamente vulnerabili al rischio di rientro ricorsivo durante queste chiamate.

Come si possono prevenire le vulnerabilità da reentrancy in Solidity?

Metodi comuni includono l'uso del pattern Checks-Effects-Interactions, il modificatore 'ReentrancyGuard' di OpenZeppelin e la minimizzazione delle chiamate esterne o l'uso di metodi di pagamento pull invece di push.

Quali strumenti aiutano a individuare vulnerabilità da reentrancy prima del deployment?

Analizzatori di sicurezza automatizzati come MythX, Slither e Securify individuano pattern di reentrancy. Audit manuali approfonditi e fuzz testing migliorano ulteriormente l'individuazione delle vulnerabilità.

Delegatecall e flash loans sono collegati agli attacchi di reentrancy?

Sì, un uso improprio di delegatecall può facilitare exploit di reentrancy eseguendo codice nel contesto di un altro contratto. I flash loan possono essere usati dagli attaccanti per amplificare l'impatto, pur non essendo di per sé un attacco reentrancy.

Chat