Come prevenire attacchi infinite di mint in Solidity

Autore dell’articolo

Un attacco di minting infinito si verifica quando un contratto di token consente a un attaccante di creare token senza autorizzazione valida, senza far rispettare un massimo di emissione, o entrambi. L’attaccante conia un saldo arbitrario, quindi vende, scambia, deposita o collateralizza i token appena creati prima che i mercati e le integrazioni possano reagire.

La conseguenza non è solo inflazione. La conia infinita può distruggere il valore di scambio di un token, prosciugare i pool di liquidità e creare collateral privo di supporto nei sistemi di prestito o di tesoreria. Elrond è stato registrato aver perso $113,0M il 2022-06-05 in un incidente classificato come Infinite Mint, mostrando come un limite di emissione rotto possa trasformarsi in una perdita a livello di ecosistema.

Cos’è un attacco di infinite mint e come funziona?

Un attacco di infinite mint sfrutta un percorso di emissione di token che non prova affidabilmente chi può coniare e quanto può esistere. Una funzione mint pubblica o mal protetta permette a un attaccante di creare saldi che il resto del protocollo tratta come token ERC-20 legittimi.

Una funzione di minting è sicura solo quando l’autorizzazione del chiamante, le condizioni di emissione e la contabilizzazione dell’offerta rispettano il design economico del protocollo. Se qualsiasi chiamante può raggiungere una logica equivalente a _mint, o un chiamante privilegiato può bypassare un limite di emissione, la scarsità del token è solo un’ipotesi.

Un attacco di minting infinito tipico segue questa sequenza:

  1. L’attaccante identifica un percorso di minting chiamabile esternamente o un controllo di accesso eludibile.
  2. L’attaccante chiama la funzione con un destinatario e un importo controllati da lui.
  3. Il contratto aumenta il saldo dell’attaccante e spesso anche totalSupply.
  4. L’attaccante trasferisce i token coniati in un pool DEX, sul mercato di prestito, tramite bridge o vault.
  5. L’attaccante scambia token artificiali per asset reali o li utilizza come collateral.
  6. I fornitori di liquidità, i prestatori e i detentori di token subiscono la perdita economica risultante.

Le vulnerabilità di infinite mint sono particolarmente pericolose dove i saldi dei token sono accettati da altri contratti senza verificare indipendentemente come siano stati creati. Un protocollo di prestito, ad esempio, non distingue tra token coniati attraverso emissioni programmate e token coniati senza un controllo di accesso.

Quali pattern di Solidity causano vulnerabilità di infinite mint?

Le vulnerabilità di infinite mint di solito derivano da mancanza di autorizzazione, assenza di limiti di emissione, o autorizzazioni che sembrano corrette ma sono raggiungibili tramite percorsi amministrativi non intenzionati. Esamina ogni funzione che può aumentare un saldo, direttamente o indirettamente.

Il pattern più ovvio è una funzione mint pubblica senza restrizioni sul chiamante:

function mint(address to, uint256 amount) external {
    balanceOf[to] += amount;
    totalSupply += amount;
}

Lo stesso rischio si presenta quando uno sviluppatore controlla l’identità sbagliata. Per esempio, tx.origin non dovrebbe definire l’autorità di minting perché un attaccante può indurre un wallet privilegiato a chiamare un contratto intermediario malicioso. Allo stesso modo, verificare se un destinatario è privilegiato non dimostra che il chiamante sia autorizzato a emettere token.

Altri cause comuni includono:

  • Una funzione setMinter chiamabile da qualsiasi account, che permette agli attaccanti di concedersi diritti di emissione.
  • Un proxy aggiornabile il cui potere di upgrade può sostituire restrizioni di minting.
  • Un bridge o gestore cross-chain che si fida di messaggi arbitrari come istruzioni di minting.
  • Una soglia (cap) applicata in una funzione di minting, ma non in altre rotte di emissione, come richieste di ricompense o funzioni di migrazione.
  • Errori di aritmetica o di conversione di unità che rendono inefficace un limite nominale.
  • Contabilizzazione di burn-and-mint in cui i burn non sono verificati prima di emettere token sostitutivi.

Una variabile totalSupply da sola non è un controllo dell’offerta. Il contratto deve rifiutare ogni mint che faccia sì che totalSupply + amount superi il massimo previsto. Questo invariato deve essere valido per ogni rotta di emissione, inclusi recuperi amministrativi, minting tramite bridge, ricompense e migrazioni.

Come appare il codice Solidity vulnerabile e corretto di infinite mint?

Un contratto vulnerabile di infinite mint espone una funzione di emissione senza restrizioni. Qualsiasi account esterno può coniare qualsiasi quantità e senza limiti di emissione il danno può essere significativo.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract VulnerableInfiniteMintToken {
    string public constant name = "Vulnerable Token";
    string public constant symbol = "VULN";

    uint256 public totalSupply;
    mapping(address => uint256) public balanceOf;

    event Transfer(address indexed from, address indexed to, uint256 amount);

    function mint(address to, uint256 amount) external {
        // VULNERABILE: qualsiasi chiamante può creare token arbitrari.
        totalSupply += amount;
        balanceOf[to] += amount;
        emit Transfer(address(0), to, amount);
    }

    function transfer(address to, uint256 amount) external returns (bool) {
        require(balanceOf[msg.sender] >= amount, "Saldo insufficiente");

        unchecked {
            balanceOf[msg.sender] -= amount;
        }
        balanceOf[to] += amount;

        emit Transfer(msg.sender, to, amount);
        return true;
    }
}

La versione corretta separa l’amministratore dai minter autorizzati e applica un massimo di emissione prima di ogni aumento di saldo. Il limite non sostituisce l’autorizzazione, né l’autorizzazione sostituisce il limite. Entrambi i controlli affrontano modalità di fallimento diverse.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract CappedRoleMintToken {
    string public constant name = "Capped Token";
    string public constant symbol = "CAP";

    address public immutable admin;
    uint256 public immutable maxSupply;
    uint256 public totalSupply;

    mapping(address => bool) public isMinter;
    mapping(address => uint256) public balanceOf;

    event Transfer(address indexed from, address indexed to, uint256 amount);
    event MinterUpdated(address indexed minter, bool allowed);

    modifier onlyAdmin() {
        require(msg.sender == admin, "non admin");
        _;
    }

    modifier onlyMinter() {
        require(isMinter[msg.sender], "non minter");
        _;
    }

    constructor(uint256 maxSupply_) {
        require(maxSupply_ > 0, "massimo di emissione zero");
        admin = msg.sender;
        maxSupply = maxSupply_;
        isMinter[msg.sender] = true;
    }

    function setMinter(address minter, bool allowed) external onlyAdmin {
        isMinter[minter] = allowed;
        emit MinterUpdated(minter, allowed);
    }

    function mint(address to, uint256 amount) external onlyMinter {
        // CORRETTO: emissione autorizzata che non supera il massimo immutabile.
        require(totalSupply + amount <= maxSupply, "superato il limite");

        totalSupply += amount;
        balanceOf[to] += amount;
        emit Transfer(address(0), to, amount);
    }

    function transfer(address to, uint256 amount) external returns (bool) {
        require(balanceOf[msg.sender] >= amount, "Saldo insufficiente");

        unchecked {
            balanceOf[msg.sender] -= amount;
        }
        balanceOf[to] += amount;

        emit Transfer(msg.sender, to, amount);
        return true;
    }
}

Se il tuo protocollo ha meccanismi di minting tramite ricompense, bridge, quote di vault o migrazioni, una auditing di smart contract e una revisione di sicurezza può tracciare ogni rotte di emissione alla sua autorizzazione e invariato dell’offerta prima del deployment.

Come prevenire attacchi di infinite mint in Solidity?

Prevenire attacchi di infinite mint richiede un design stratificato di emissione: autorità esplicita, vincoli di offerta applicati, poteri amministrativi ristretti e test che dimostrino che nessun’altra via può creare saldi. Il design appropriato dipende dal tipo di offerta: fissa, limitata, basata su emissioni, collateralizzata o basata su bridge.

Tecnica Cosa impedisce Costo in gas o complessità Limite
mint con ruolo Minting da chiamanti esterni arbitrari Lettura di storage per controllo ruolo Un minter compromesso può ancora mintare entro la sua autorità
Supposto massimo immutabile Mint oltre il massimo previsto Un check aritmetico per ogni mint Non impedisce a un soggetto autorizzato di mintare prematuramente l’intera quota
Ruoli separati di amministratore e minter Un minter si concede silenziosamente più autorità Codice di gestione ruoli aggiuntivo L’amministratore rimane un confine di fiducia critico
Gestione multisignature o a time lock Cambiamenti individuali ai minter o configurazioni di emissione Ritardo operativo e sovrastruttura di governance Non corregge logiche di minting difettose già deployate
Percorso centralizzato _mint interno Mancata verifica del limite in funzioni secondarie Riprogettazione necessaria I revisori devono ancora verificare tutte le rotte esterne verso _mint
Monitoraggio eventi di attività di mint Rilevamento tardivo di emissioni anomale Monitoraggio off-chain Il monitoraggio non può annullare un mint già valido e dannoso

Per token a offerta fissa, il pattern più forte è spesso coniare l’intera allocazione durante il deploy e rimuovere tutte le capacità di mint successivo. Per token basati su emissioni, definire un programma misurabile, applicare il limite aggregato nella funzione condivisa di emissione e assicurarsi che ogni funzione esterna abbia una autorizzazione distinta.

La metodologia di Soken considera il mint come un limite alla creazione di asset, non come una funzione di convenienza. Ogni rotta che aumenta i saldi di token dovrebbe avere un modello di autorizzazione documentato, un limite economico esplicito e un invariato verificabile.

Quali incidenti reali sono stati classificati come attacchi di infinite mint?

Gli incidenti di infinite mint registrati mostrano che i difetti di emissione di token interessano asset consolidati e protocolli più recenti su diverse chains. Gli incidenti sottostanti sono classificati come Infinite Mint nel database di hack di DefiLlama.

Protocollo Data Importo perso Chain
Elrond 2022-06-05 $113.0M Elrond
Paid Network 2021-03-05 $27.0M Ethereum
Gala 2024-05-20 $22.3M Ethereum
Haven Protocol 2021-07-03 $8.2M Haven
OcelotDex 2024-09-20 $4.9M Ethereum
TSR 2026-06-01 $2.5M BSC
Raydium AMM 2026-06-10 $1.3M Solana
Flooring Protocol 2026-06-08 $500K Ethereum

Questi incidenti condividono una proprietà economica fondamentale: una volta che token non supportati entrano in un mercato integrato, l’attacco si estende oltre il contratto del token. I pool di liquidità acquistano l’offerta artificiale, i mercati di prestito possono accettarla come collateral, e bridge o vault possono propagate il saldo in sistemi ulteriori.

La domanda di revisione pertinente è dunque più ampia di “Un attaccante può chiamare mint?” È piuttosto: “Può qualsiasi percorso non affidabile o eccessivamente affidabile causare che il protocollo riconosca token non supportati come asset validi?”

Come si rileva una vulnerabilità di infinite mint prima del deployment?

Rilevare vulnerabilità di infinite mint richiede mappare tutte le transizioni di stato che aumentano i saldi, dimostrando poi che ogni transizione è autorizzata e preserva l’invariante di emissione previsto. Una revisione manuale dovrebbe cominciare dai contratti di token, ma deve continuare in bridge, vault, distributori di ricompense, moduli di upgrade e helper amministrativi.

Segui questa checklist di revisione:

  1. Trova ogni scrittura su saldi, quote, contatori di offerta e funzioni di minting interne.
  2. Verifica che ogni rotta di emissione esterna abbia l’autorizzazione del chiamante appropriata.
  3. Conferma che ogni rotta applichi lo stesso limite o vincolo di emissione.
  4. Rivedi funzioni che aggiungono, rimuovono o sostituiscono minter e amministratori.
  5. Traccia i percorsi di upgrade, bridge, migrazione e callback che possono raggiungere la logica di minting.
  6. Controlla se account privilegiati possono cambiare inaspettatamente il limite, l’emittente o l’unità di contabilità.
  7. Verifica che gli eventi di mint e totalSupply corrispondano alla reale creazione di saldi.
  8. Testa le integrazioni che accettano il token come collateral, asset di liquidità o vault.

Slither è utile per elencare funzioni chiamabili esternamente, cambiamenti di stato privilegiati e pattern sospetti di controllo accesso, ma non può stabilire che il modello di offerta di un protocollo sia economicamente corretto. La ragionamento manuale è essenziale per le rotte di minting indirette e per l’autorità tra contratti.

Il fuzzing di Foundry può testare che chiamanti arbitrari non possano mintare e che l’offerta non superi mai il limite. I test di invariato sono particolarmente efficaci perché esprimono direttamente la proprietà di sicurezza:

function invariant_supplyNeverExceedsCap() public view {
    require(token.totalSupply() <= token.maxSupply(), "superato il limite di offerta");
}

Una seconda invariato dovrebbe assicurare che solo attori approvati possano aumentare l’offerta. Nella metodologia di revisione di Soken, questa proprietà viene testata su tutte le funzioni pubbliche, non solo sulla funzione mint. Per approfondimenti su controllo degli accessi, accounting dei token e rischi correlati, visita il Soken Hub.

Insight di sicurezza: La metodologia di Soken tratta ogni nuova unità di token come un obbligo creato dal protocollo. Se il contratto non riesce a dimostrare chi ha autorizzato quell’obbligo e perché rimane all’interno del modello di emissione, il percorso di minting diventa un confine di sicurezza che richiede redesign.

Un controllo di ruolo e un limite di offerta trasformano un’emissione illimitata in un’operazione limitata e responsabile, ma solo se utilizzati in ogni rotta di mint. L’attività critica di ingegneria è centralizzare la creazione di saldi e rendere impossibile bypassare le restrizioni tramite costruzione.

Prima del lancio o aggiornamento, scrivi in linguaggio semplice l’invariante di offerta prevista, implementalo nel percorso condiviso di minting e fallalo con test di invariato di Foundry.

Autore dell’articolo

Domande frequenti

Cos'è un attacco di mint infinito in Solidity?

Un attacco di mint infinito in un contratto Solidity si verifica quando un attacker può creare token senza autorizzazione valida o senza un limite massimo di offerta, ottenendo un saldo arbitrario trattato come token ERC-20 legittimo.

Come funziona un attacco di mint infinito?

L'attacco sfrutta un percorso di emissione dei token che non dimostra chi può coniare i token o la quantità massima di offerta. Una funzione di mint pubblica o poco protetta permette di creare bilanci considerati legittimi dal protocollo.

Quali danni può causare un attacco di mint infinito?

Può distruggere il valore di scambio di un token attraverso inflazione incontrollata, con token appena mintati venduti, scambiati o usati come collaterale, drenando liquidity pools e creando collateral non garantito.

Cosa è successo nell'incidente di mint infinito di Elrond?

Elrond ha subito una perdita di 113 milioni di dollari il 5 giugno 2022. L'incidente evidenzia come una falla nel token issuance possa causare perdite a livello di ecosistema se le nuove emissioni vengono accettate dai mercati e dalle integrazioni.

Come possono i sviluppatori Solidity prevenire attacchi di mint infinito?

Devono garantire che ogni percorso di mint sia protetto da autorizzazioni valide e limiti di offerta massima, così da impedire la creazione arbitraria di bilanci di token da parte degli attacker.

Chat