Come prevenire attacchi Broken Access Control in Solidity

Autore dell’articolo

Gli attacchi di broken access control si verificano quando un contratto Solidity permette a un chiamante non affidabile di accedere a capacità che dovrebbero essere riservate, come spostare asset, modificare un’implementazione, assegnare ruoli o configurare un indirizzo di fiducia. L’attaccante non ha bisogno di violare la crittografia: sfrutta un controllo di autorizzazione mancante, errato o troppo ampio per ottenere una escalation di privilegi efficace.

L’impatto può interessare l’intero protocollo perché le funzioni privilegiate spesso controllano fondi collettivi o configurazioni critiche per la governance. Poly Network ha perso 611 milioni di dollari il 10-08-2021 tra Ethereum, BSC e Polygon in un incidente classificato da DefiLlama come Improper Access Control. L’autorizzazione non è quindi una questione UI o un dettaglio amministativo: fa parte del confine di sicurezza centrale del contratto.

Cos’è una vulnerabilità di broken access control in Solidity?

Una vulnerabilità di broken access control si verifica quando un contratto non verifica in modo affidabile che msg.sender sia autorizzato a eseguire un’azione privilegiata, o quando il modello di autorizzazione concede più privilegi di quelli necessari per quella azione. In Solidity, il fallimento si manifesta solitamente in funzioni chiamabili esternamente che modificano dati sensibili, trasferiscono asset, aggiornano il codice oppure cambiano controparti di fiducia.

Un contratto può essere vulnerabile anche quando ha una variabile owner. La domanda critica è se ogni percorso di esecuzione privilegiato verifica correttamente questa autorità, comprese le funzioni helper, gli initializer, le funzioni di gestione ruoli e i hook di upgrade.

Un attacco tipico di broken access control segue questa sequenza:

  1. L’attaccante identifica una funzione chiamabile esternamente con conseguenze privilegiate.
  2. La funzione non ha controlli sul chiamante, verifica l’indirizzo sbagliato o si affida a uno stato controllato dall’attaccante.
  3. L’attaccante chiama direttamente la funzione, bypassando eventuali restrizioni di frontend.
  4. La chiamata concede un ruolo, cambia un indirizzo di fiducia, o invoca un’operazione di spostamento asset.
  5. L’attaccante utilizza la capacità appena acquisita per prelevare fondi o modificare il comportamento del protocollo.
  6. Il contratto accetta la transazione perché l’EVM applica solo controlli on-chain.

I bottoni del frontend, la documentazione e le procedure operative previste non limitano le chiamate dirette al contratto. Qualunque account può inviare calldata a una funzione pubblica o esterna. Pertanto, il codice Solidity deve imporre questa politica nel punto esatto in cui avviene una transizione di stato sensibile.

Quali pattern di Solidity causano attacchi di access control e escalation di privilegi?

Le vulnerabilità di improper access control emergono comunemente quando gli sviluppatori espongono impostatori di configurazioni, helper amministrativi o percorsi di inizializzazione senza considerare che ogni funzione pubblica può rappresentare un’API rivolta all’attaccante. Il pattern pericoloso non si limita a una mancanza totale di onlyOwner: include autorizzazioni che sono mutabili da chiamanti arbitrari, ereditate in modo errato, applicate in modo incoerente o troppo ampie rispetto all’operazione.

I pattern di fallimento più frequenti includono:

  • Setter pubblici che permettono a chiunque di sostituire un treasury, un oracle, un’implementazione, un operatore o un beneficiario.
  • Funzioni di gestione ruoli prive di controlli da parte di un amministratore.
  • Initializer chiamabili post-deploy o chiamati da account non previsti.
  • Funzioni che verificano tx.origin invece di msg.sender.
  • Funzioni interne privilegiate raggiunte tramite un wrapper esterno non protetto.
  • Un singolo ruolo di amministratore che può eseguire tutte le operazioni operative, aumentando il raggio di compromissione.
  • Autorizzazioni basate su un indirizzo fornito dall’utente invece di una mappatura di ruoli immutabili o controllati.
  • Funzioni di upgrade che validano il chiamante ma non la nuova implementazione o il percorso di upgrade.

Questo pattern difettoso è particolarmente sottile:

function setOperator(address newOperator) external {
    operator = newOperator;
}

La funzione di pagamento può richiedere correttamente msg.sender == operator, ma il limite di accesso è già fallito perché chiunque può nominarsi operatore. I revisori dovrebbero tracciare all’indietro da ogni funzione sensibile per identificare chi può ottenere il ruolo richiesto.

La metodologia di Soken considera l’assegnazione dei ruoli e la configurazione degli indirizzi di fiducia operazioni privilegiate a tutti gli effetti. Un design di controllo accessi sicuro protegge sia il trasferimento finale di asset sia ogni percorso che può sbloccarlo.

Come appare un attacco di broken access control nel codice Solidity?

Questo vault vulnerabile verifica il chiamante prima di un payout, ma la funzione setPayoutOperator è pubblica. Un attaccante può assegnarsi come operatore e successivamente invocare pay, rendendo inutile il check di autorizzazione successivo.

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

contract VulnerablePayoutVault {
    address public payoutOperator;

    constructor(address initialOperator) {
        payoutOperator = initialOperator;
    }

    receive() external payable {}

    function setPayoutOperator(address newOperator) external {
        // VULNERABILE: qualsiasi chiamante può concedersi autonomia di payout.
        payoutOperator = newOperator;
    }

    function pay(address payable recipient, uint256 amount) external {
        require(msg.sender == payoutOperator, "not operator");
        require(address(this).balance >= amount, "insufficient balance");

        (bool sent, ) = recipient.call{value: amount}("");
        require(sent, "payment failed");
    }
}

La soluzione separa amministrazione e operazioni. Solo il proprietario può concedere o revocare il ruolo di payout ristretto, mentre un operatore payout non può amministrare ruoli o cambiare proprietà.

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

contract FixedPayoutVault {
    address public owner;
    mapping(address => bool) public payoutOperators;

    modifier onlyOwner() {
        require(msg.sender == owner, "not owner");
        _;
    }

    modifier onlyPayoutOperator() {
        require(payoutOperators[msg.sender], "not payout operator");
        _;
    }

    constructor(address initialOwner, address initialOperator) {
        require(initialOwner != address(0), "zero owner");
        require(initialOperator != address(0), "zero operator");
        owner = initialOwner;
        payoutOperators[initialOperator] = true;
    }

    receive() external payable {}

    function setPayoutOperator(address operator, bool allowed) external onlyOwner {
        require(operator != address(0), "zero operator");
        // CORREZIONE: solo il proprietario può modificare l'appartenenza ai ruoli privilegiati.
        payoutOperators[operator] = allowed;
    }

    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0), "zero owner");
        owner = newOwner;
    }

    function pay(address payable recipient, uint256 amount)
        external
        onlyPayoutOperator
    {
        require(address(this).balance >= amount, "insufficient balance");
        (bool sent, ) = recipient.call{value: amount}("");
        require(sent, "payment failed");
    }
}

Se nel sistema sono previsti cambi di ruolo, permessi di upgrade o prelievi dal treasury, una audit di smart-contract focalizzata sul controllo accessi e una valutazione di sicurezza possono verificare che tutti i percorsi verso queste capacità siano correttamente vincolati.

Come possono i sviluppatori Solidity prevenire vulnerabilità di improper access control?

Prevenire attacchi di broken access control richiede un modello di permessi esplicito, non una serie di require sparse aggiunte a posteriori. Definire quale attore può eseguire ogni operazione sensibile, perché tale autorizzazione esiste e quale ruolo più piccolo può svolgere in sicurezza le attività di routine.

Tecnica Cosa blocca Costo di gas o complessità Limite
Modificatore onlyOwner Gestione amministrativa non autorizzata di funzioni owner-only Una lettura di storage per chiamata protetta Un owner compromesso mantiene tutti i privilegi
Mappature di ruoli o AccessControl di OpenZeppelin Uso non autorizzato di ruoli operativi separati Letture di storage e logica di gestione ruoli Un design di ruolo poco preciso può creare privilegi eccessivi
Ruoli a minimo privilegio Un keeper o operatore con poteri amministrativi non correlati Più funzioni e definizioni di ruolo da mantenere Richiede limiti chiari di operatività
Timelock per cambi critici Aggiornamenti o modifiche di parametri immediati e malevoli Esecuzione differita e contratti aggiuntivi Non impedisce azioni malevole se gli utenti non rispondono al delay
Amministrazione multisig Una singola chiave che modifica unilateralmente lo stato privilegiato Coordinamento dei firmatari e sovraccarico operativo Signer condivisi o politiche deboli rimangono rischi
Guardie di inizializzazione single-time Inizializzazione non autorizzata o ripetuta Flag di storage e logica di initializer Le procedure di deploy devono ancora impostare l’autorità desiderata

Utilizzare primitive di ownership e ruoli ben revisionate dove opportuno, ma non considerare una libreria come un modello di sicurezza completo. Ad esempio, Ownable di OpenZeppelin definisce meccaniche di proprietà, ma il team di protocollo deve ancora decidere se la proprietà debba controllare pause, upgrade, raccolta fee, sostituzione oracle o recupero di emergenza.

Evita tx.origin per l’autorizzazione: può essere manipolato tramite contratti intermediari e non identifica immediatamente il chiamante diretto. Usa msg.sender e verifica rispetto a un owner, ruolo, multisig o regola di autorizzazione specifica del contratto.

Quali incidenti reali dimostrano l’impatto di attacchi di access control?

DefiLlama registra 130 incidenti di smart-contract classificati come Improper Access Control tra il 2020 e il 2026, escludendo breach di exchange e wallet, con perdite complessive di 909,7 milioni di dollari. Di seguito alcune di queste verifiche confermate, che mostrano come i fallimenti di access control coinvolgano bridge, protocolli DeFi, bot e applicazioni su più catene.

Protocollo Data Importo Chain
Poly Network 2021-08-10 $611,0M Ethereum, BSC, Polygon
Mirror 2021-10-08 $90,0M Terra
Wintermute 2022-06-05 $17,6M Optimism
ValueDefi 2021-05-05 $10,0M BSC
Cork V1 2025-05-28 $12,0M Ethereum
SUPERFORTUNE AI 2026-05-27 $15,2M Ethereum
The Sandbox 2026-08-22 $675K Base, BSC, Ethereum
Radix 2026-08-31 $1,2M Radix

Questi incidenti portano a una lezione fondamentale di sicurezza: il valore economico di un protocollo può essere esposto quando il confine di autorità di un contratto non corrisponde al modello di fiducia previsto. La funzione interessata può variare, ma il fallimento è sempre lo stesso: un attore ottiene o esercita un’autorità che il design avrebbe dovuto trattenere.

Come individuare le vulnerabilità di broken access control prima del deploy?

Rilevare i problemi di access control richiede mappare le modifiche di stato sensibili rispetto alle loro condizioni di autorizzazione e dimostrare che nessun percorso alternativo può bypassarle. Strumenti automatizzati aiutano a identificare pattern sospetti, ma la revisione umana è essenziale per comprendere le assunzioni di business, le fiducie e le catene di escalation privilegi.

Segui questa checklist:

  1. Elenca tutte le funzioni che trasferiscono asset, modificano ruoli, aggiornano codice, mettono in pausa o cambiano indirizzi di fiducia.
  2. Identifica il chiamante previsto e la regola di autorizzazione per ogni funzione.
  3. Traccia ogni funzione che può concedere, revocare, trasferire o inizializzare quella autorità.
  4. Controlla tutte le funzioni wrapper esterne che raggiungono funzioni interne privilegiate.
  5. Verifica che l’inizializzazione possa avvenire una sola volta e solo nel processo di deploy previsto.
  6. Controlla che i ruoli di emergenza non possano eseguire azioni di routine o di governance non correlate.
  7. Testa il revoking di ruoli, il trasferimento di proprietà e la gestione dello zero address.
  8. Esamina proxy, contratti ereditati e percorsi di delegatecall come parte dello stesso boundary di autorizzazione.

Slither può segnalare pattern di autorizzazione mancante, uso pericoloso di tx.origin e modificatori incoerenti, ma i risultati richiedono validazione manuale. Foundry fuzzing aiuta a generare chiamate da indirizzi arbitrari e verificare che chiamanti non autorizzati non possano modificare stati privilegiati.

I test invarianti sono particolarmente efficaci. Per esempio, un’invariante di vault può affermare che un indirizzo senza il ruolo payout non deve mai far diminuire il saldo del vault tramite pay. Un’altra invariante può stabilire che solo il proprietario può modificare i membri di ruolo. Questi test verificano direttamente la regola di sicurezza, piuttosto che i semplici flussi d’utente attesi.

Approccio di sicurezza, metodologia Soken: Revisiona l’autorizzazione come un grafo di capacità, non come una lista di modificatori: ogni risultato privilegiato deve avere un percorso deliberate e verificabile verso l’autorità, e ogni percorso non previsto rappresenta una potenziale via di escalation.

Gli attacchi di broken access control si evitano rendendo l’autorità esplicita, riducendo al minimo i poteri di ciascun ruolo e proteggendo le funzioni che assegnano tali poteri con attenzione quanto le funzioni che movono fondi. Una funzione di prelievo correttamente limitata non è sicura se chiunque possa nominare il prelevatore.

Inizia creando una mappa dell’autorità dei tuoi contratti deployati e in progetto, comprese proxy e account operativi, e trasforma ogni regola in un modificatore e in un test invariante. Per approfondimenti sulla sicurezza Solidity e vulnerabilità correlate, esplora il Soken Hub.

Autore dell’articolo

Domande frequenti

Cos'è una vulnerabilità di broken access control in Solidity?

Una vulnerabilità di broken access control in Solidity si verifica quando un contratto non conferma affidabilmente che msg.sender sia autorizzato a compiere un'azione privilegiata. Permette a un chiamante non affidabile di raggiungere capacità che modificano lo stato, potenzialmente causando escalation di privilegio.

Come creano gli attacchi broken access control un'escalation di privilegi?

Gli attacchi broken access control creano escalation di privilegi quando un contratto Solidity concede a un chiamante non affidabile accesso a capacità riservate a un'entità autorizzata. L'attaccante può eseguire azioni di modifica dello stato, come muovere asset, assegnare ruoli o configurare indirizzi fidati.

Quali azioni in Solidity dovrebbero essere limitate dall'access control?

L'access control in Solidity dovrebbe limitare ogni capacità di modificare lo stato non destinata a chiamanti arbitrari. Esempi critici includono il trasferimento di asset, la modifica dell'implementazione del contratto, l'assegnazione di ruoli e la configurazione di indirizzi di fiducia. Ogni capacità necessita di un'autorizzazione affidabile collegata a msg.sender.

Cosa è successo nell'incidente di access control di Poly Network?

Il 10 agosto 2021, Poly Network ha perso 611 milioni USD su Ethereum, BSC e Polygon in un evento classificato da DefiLlama come Improper Access Control. Mostra l'importanza di verifiche di autorizzazione che proteggono più di soli aspetti amministrativi, poiché capacità privilegiate possono influenzare fondi e configurazioni di governance.

Perché l'autorizzazione è un perimetro di sicurezza fondamentale in Solidity?

L'autorizzazione è un perimetro di sicurezza chiave in Solidity perché funzioni privilegiate possono controllare fondi pooled e configurazioni critiche di governance. Una verifica di autorizzazione mancante, errata o troppo permissiva permette a chiamanti non affidabili di accedere a capacità riservate, trasformando una debolezza amministrativa in un rischio di sicurezza protocollo.

Chat