Home
» Kennis
»
Een stapsgewijze handleiding voor het controleren van het smart contract van een cryptoproject
Een stapsgewijze handleiding voor het controleren van het smart contract van een cryptoproject
Een smart-contractaudit is een gestructureerde poging om te ontdekken hoe een contract kan falen, misbruikt kan worden of op een manier beheerd kan worden die gebruikers niet hadden verwacht. Het is niet hetzelfde als het uitvoeren van één scanner, het lezen van een auditbadge of het controleren of de broncode geverifieerd is. Gebruik de onderstaande workflow om een geïmplementeerd EVM-contract of een codebase te beoordelen voordat u er aanzienlijke bedragen aan toevertrouwt.
Belangrijk: Dit is een praktisch beoordelingskader, geen garantie dat een project veilig is en geen beleggingsadvies. Een productieprotocol met aanzienlijke waarde dient onafhankelijk te worden beoordeeld door ervaren beveiligingsprofessionals. De afbeeldingen in deze handleiding zijn illustratief en mogen niet worden beschouwd als bewijs voor een specifiek project of implementatie.
Auditchecklist in één oogopslag
Stap
Hoofdvraag
Nuttig bewijs
1. Omvang
Beoordeel ik wel precies het contract dat gebruikers gebruiken?
Blijft het gedrag consistent bij onverwachte invoer en opeenvolging van gebeurtenissen?
Eenheids-, fuzz-, invariant- en fork-tests
8. Rapportage
Kan een andere persoon het resultaat reproduceren en opnieuw testen?
Bevinding, impact, bewijs, oplossing en status van hertesten
Stap 1: Bevestig de reikwijdte en het geïmplementeerde artefact
Een weergave van de contractverificatie die de netwerk-, adres-, compilerversie- en bytecode-matchcontroles toont die vóór de analyse moeten worden vastgelegd.
Begin met de exacte blockchain en het adres. Noteer het implementatieadres, de transactiehash, het bloknummer, de compilerversie, de optimizerinstellingen, de constructorargumenten en de commit of release die volgens het team is geïmplementeerd. Een project kan meerdere adressen hebben voor een token, router, vault, proxy, implementatie, oracle of testimplementatie. Het controleren van een onjuist adres heeft geen praktische waarde.
Controleer of de geverifieerde broncode van de verkenner overeenkomt met de geïmplementeerde bytecode. Verificatie is nuttig omdat u hiermee de broncode en ABI kunt inspecteren, maar het is slechts een identiteitscontrole: het bewijst niet dat de bedrijfslogica veilig is. Als het contract upgradebaar is, identificeer dan zowel de proxy als de huidige implementatie. Lees het implementatieadres uit het gedocumenteerde mechanisme van de proxy of de informatie van de verkenner en bevestig vervolgens dat de implementatie de implementatie is die u wilt beoordelen. De officiële Foundry-verificatiehandleiding van Etherscan beschrijft de verificatie voor nieuwe en bestaande contracten.
Definieer ook de grenzen. Neem geïmporteerde bibliotheken, overgeërfde contracten, gekoppelde bibliotheken, geïmplementeerde hulpcontracten, oracle-adapters, van gebruikers ontvangen tokens en geprivilegieerde off-chain componenten mee. Beschrijf wat buiten de scope valt en waarom. Dit voorkomt dat een beperkte beoordeling ten onrechte wordt aangezien voor een beoordeling van het volledige systeem.
Stap 2: Stel een toegangspunt en een overzicht van de activa samen
Een functie-inventaris die openbare en externe toegangspunten scheidt voordat de beoordelaar hun statuswijzigingen in kaart brengt.
Geef een lijst van alle openbare en externe functies, inclusief overgeërfde functies en fallback- of receive-handlers. Noteer voor elke functie of deze het volgende kan:
Het verplaatsen van de eigen valuta of tokens;
munten, verbranden, lenen, liquideren of de boekhouding wijzigen;
Een orakel, vergoeding, limiet, rol, pauzestatus of implementatie wijzigen;
Een extern gesprek voeren, een gesprek delegeren of een gesprek op laag niveau voeren; of
Lees gegevens waar een andere functie die de status wijzigt, op vertrouwt.
Breng vervolgens de activa en vertrouwensgrenzen in kaart. Volg een storting van de gebruiker naar de opslag, via prijsbepaling en aandelenberekening, tot aan de opname. Identificeer elk adres dat door een beller wordt verstrekt en elk adres dat vanuit de opslag wordt geladen. Vraag welke waarden als betrouwbaar worden beschouwd: een orakel, een token, een bridgebericht, een keeper, een callback-ontvanger of een beheerder. De belangrijkste controledoelen zijn functies die gebruikersgestuurde invoer, geprivilegieerde status, rekenkundige bewerkingen en een externe aanroep combineren.
Stap 3: Compileer zonder fouten en voer een statische analyse uit.
Een statische analyse kan snel potentiële problemen aan het licht brengen, die echter nog moeten worden bevestigd aan de hand van de daadwerkelijke code en het dreigingsmodel.
Reproduceer de build van het project met de opgegeven Solidity-versie, afhankelijkheidsversies, optimizerconfiguratie en aannames over de doelketen. Beschouw compilerwaarschuwingen als beoordelingspunten in plaats van als onschadelijke ruis. De beveiligingsrichtlijnen van Solidity bevelen specifiek aan om waarschuwingen serieus te nemen, contracten begrijpelijk te houden en bekende compilerproblemen te controleren. Raadpleeg de officiële lijst met bekende Solidity-compilerfouten wanneer de compilerversie of de betreffende codefragmenten dit vereisen.
Voor een Hardhat-, Foundry- of vergelijkbaar project start je Slither vanuit de projectmap. De officiële documentatie beschrijft de tool als een statische analysetool voor Solidity en Vyper en geeft het volgende commando:
slither .
Sla de uitvoer op en beoordeel elk resultaat op basis van impact en betrouwbaarheid. Besteed extra aandacht aan bevindingen met betrekking tot willekeurige tokenverzendingen, onbeveiligde upgrades, re-entrancy, ongecontroleerde retourwaarden, gevaarlijke delegatecalls, tx.origin, zwakke willekeurigheid en onjuiste interfaces. Een detector kan een vals positief resultaat geven, een projectspecifieke economische fout missen of code signaleren die elders opzettelijk beperkt is. Statische analyse verkleint de zoektocht; het vervangt geen handmatige redenering. De Slither-repository en -documentatie bevatten ook printers voor toegangspunten, autorisatie, oproepgrafieken en contractoverzichten die helpen bij het structureren van een beoordeling.
Stap 4: Externe aanroepen en re-entrancy handmatig traceren
Een extern gesprek wordt gemarkeerd vóór een saldo-update, waarmee de volgordevraag wordt geïllustreerd die een beoordelaar in elk opnameproces moet controleren.
Stop elke externe aanroep en traceer de status vóór, tijdens en na de aanroep. De aangeroepen partij kan een kwaadwillig contract zijn, een token met hooks, een callback-ontvanger of een ander protocol dat een gedeelde afhankelijkheid wijzigt. De documentatie van Solidity legt uit dat een interactie met een ander contract de controle aan dat contract kan overdragen en beveelt het Checks-Effects-Interactions-patroon aan: eerst valideren, vervolgens de status van dit contract bijwerken en als laatste extern interageren.
Beperk de zoektocht niet tot voor de hand liggende Ether-transfers. Controleer hooks in ERC-777-stijl, callbacks in ERC-1155-stijl, flash-loan callbacks, willekeurige routers, oracle-aanroepen en aanroepen via overgeërfde bibliotheken. Controleer re-entrancy tussen functies en contracten: een callback kan een andere functie aanroepen die een tussenliggende status leest. Bevestig dat elke aanroep op laag niveau het succesresultaat controleert en een geretourneerde waarde correct verwerkt. Vraag je af of een mislukte ontvanger opnames permanent kan blokkeren of een oneindige lus kan veroorzaken.
Leg voor elk plausibel probleem een concrete aanvalssequentie vast. Bijvoorbeeld: de aanvaller stort geld, start een opname, ontvangt een terugbelverzoek, start een tweede opname en laat pas daarna het eerste verzoek voltooien. Als de sequentie niet kan werken vanwege een specifieke invariant of beveiliging, noteer dan de reden. Dit maakt de conclusie controleerbaar in plaats van speculatief.
Stap 5: Test de rekenkundige en bedrijfsinvarianten
Een checklist voor rekenkunde en bedrijfslogica belicht de randgevallen die vaak over het hoofd worden gezien bij standaardtests voor het beste resultaat.
Controleer de betekenis van elke eenheid en omrekening: wei versus ether, decimalen van tokens, basispunten, aandelen versus activa, getekende waarden en tijdseenheden. Volg de afrondingsrichting. Een deling die afrondt in het voordeel van een deposant, lener, liquidator of ontvanger van een vergoeding kan waardeverlies veroorzaken bij herhaling. Controleer vermenigvuldiging vóór deling, minimum- en maximumbedragen, vergoedingslimieten, verouderde prijzen, nulvoorraad, nulsaldo en de eerste deposant of laatste opnemer.
Solidity 0.8 en latere versies detecteren normaal gesproken rekenkundige overloop en onderloop, maar code binnen een uncheckedcodeblok wijzigt dit gedrag opzettelijk. Gecontroleerde rekenkunde kan er ook voor zorgen dat een protocol terugvalt op een eerdere versie of onbruikbaar wordt als de limieten niet correct zijn ontworpen. Test beide mogelijke uitkomsten: diefstal of onjuiste boekhouding, en denial of service veroorzaakt door een waarde die nooit verwerkt kan worden.
Formuleer invarianten in eenvoudige taal voordat je ze in tests omzet. Voorbeelden zijn: "het totale aantal aandelen komt overeen met de activa volgens de aangegeven afrondingsregel", "een gebruiker kan niet meer opnemen dan zijn geregistreerde claim", "het totale tokenaanbod is gelijk aan de som van de saldi waar dat model van toepassing is" en "een vergoeding kan de geconfigureerde limiet niet overschrijden". Vergelijk de opslagsaldi met de werkelijke tokensaldi, omdat tokens direct naar een contract kunnen worden verzonden of zich anders kunnen gedragen dan de veronderstelde ERC-20-implementatie.
Stap 6: Controleer de machtigingen en de mogelijkheid tot upgraden.
Bij de beoordeling van de bevoegdheden moet elke rol gekoppeld worden aan het bijbehorende adres, de toegestane actie, het overdrachtsproces en het upgradepad.
Stel een privilege-matrix op. Identificeer voor elke administratieve functie de vereiste rol, de huidige houder, het overdrachtsmechanisme, de vertraging, multisignature of governance-controle en het noodgedrag. Besteed bijzondere aandacht aan het aanmaken, pauzeren, wijzigen van kosten, wijzigen van oracle-bronnen, het redden van fondsen, het upgraden van code en het wijzigen van vertrouwde token- of routeradressen. De documentatie over toegangscontrole van OpenZeppelin maakt onderscheid tussen eenvoudig eigenaarschap en op rollen gebaseerde machtigingen en beschrijft het principe van minimale privileges als een nuttige beveiligingspraktijk.
Maak onderscheid tussen "de code staat een beheerder toe dit te doen" en "een willekeurige gebruiker kan dit doen". Het eerste kan een expliciet risico vormen voor governance of beheer; het tweede is een kwetsbaarheid in de autorisatie. Controleer of rolcontroles elk gevoelig pad bestrijken, inclusief interne helpers die bereikbaar zijn vanuit openbare functies. Controleer of een standaardbeheerder zichzelf of anderen extra bevoegdheden kan verlenen en of de overdracht van eigendom per ongeluk naar een onbruikbaar adres kan worden verzonden.
Voor proxies is het belangrijk om de initialisatiefunctie, implementatieautorisatie, upgradevertraging, opslagindeling en het terugdraai- of noodplan te controleren. De richtlijnen van OpenZeppelin voor upgradebare contracten leggen uit waarom constructors de proxyopslag niet initialiseren, waarom initialisatiefuncties beveiligd moeten zijn, waarom een implementatie niet ongeïnitialiseerd mag blijven en waarom het wijzigen van de opslagvolgorde of -typen een upgrade kan verstoren. Beschouw een proxy-beheerderssleutel als onderdeel van de beveiligingsgrens van het protocol, niet als een implementatiedetail.
Stap 7: Test het systeem met fuzzing, invarianten en forks.
Voorbijgaande campagnes zijn nuttig bewijsmateriaal, terwijl een tegenvoorbeeld precies laat zien welke reeks nader onderzoek vereist.
Voer unit tests uit voor het verwachte gedrag en voeg vervolgens negatieve tests toe voor ongeautoriseerde bellers, nulwaarden, maximumwaarden, verlopen handtekeningen, verouderde orakelgegevens, mislukte overdrachten en herhaalde bewerkingen. Test de invoergegevens met behulp van fuzzing in plaats van slechts een paar zorgvuldig geselecteerde getallen te testen. Neem meerdere actoren en kwaadwillende ontvangercontracten op waar het ontwerp callbacks toelaat.
Gebruik invarianttesten voor eigenschappen die na veel willekeurige aanroepen waar moeten blijven. De documentatie van Foundry over invarianttesten beschrijft willekeurige sequenties, gefuzzde invoer, runs, diepte, doelcontracten en doelverzenders. Configureer handlers zodat aanroepen betekenisvol zijn; als elke gefuzzde storting terugdraait omdat de testactor geen tokens heeft, kan een geslaagde invariant simpelweg betekenen dat er geen nuttige statuswijziging heeft plaatsgevonden.
Gebruik waar mogelijk een fork van het doelnetwerk om de geïmplementeerde adressen, de huidige configuratie, het tokengedrag en de proxy-routering te testen. Houd fork-tests veilig en alleen-lezen, tenzij u een geïsoleerde lokale fork gebruikt. Minimaliseer elke mislukte sequentie en bewaar het tegenvoorbeeld, de aanroeperadressen, de blokcontext, de saldi en de relevante opslagwaarden. Een geslaagde test is bewijs voor de geteste paden, niet voor alle mogelijke paden.
Stap 8: Noteer de bevindingen die verholpen en opnieuw getest kunnen worden.
Een nuttig rapport koppelt de ernst en de status aan bewijsmateriaal, een specifieke oplossing en een hertest.
Gebruik één document per probleem. Een praktische bevinding moet het volgende bevatten:
Titel en locatie: contract, functie, bestand en regel- of codeverwijzing.
Impact: wat kan er gestolen, bevroren, opgeblazen, omzeild of onjuist gemaakt worden.
Voorwaarde: de benodigde machtigingen, balansen, timing of configuratie.
Reproductie: een korte transactiereeks, test, trace of bewijs.
Aanbeveling: een specifieke code- of operationele wijziging, met afwegingen.
Status: open, opgelost, beperkt, risico geaccepteerd of niet reproduceerbaar.
Hertest: de exacte test of observatie die de oplossing bevestigt.
De ernst van een probleem moet de realistische impact en exploiteerbaarheid weerspiegelen, niet hoe alarmerend een codefout eruitziet. Leg de aannames uit. Een aanroep op laag niveau kan veilig zijn achter een sterke invariant; een ogenschijnlijk gewone parameterwijziging kan kritiek zijn als deze een oracle of upgrade aanstuurt. Bekijk na een oplossing de verschillen, voer de relevante test opnieuw uit, voer de volledige testsuite opnieuw uit en controleer op regressies. Als het geïmplementeerde adres al is geüpgraded of gewijzigd, test dan de daadwerkelijke on-chain implementatie en configuratie opnieuw.
Veelvoorkomende auditfouten die u moet vermijden
“De broncode is geverifieerd, dus is deze veilig.” Verificatie stelt vast of er overeenstemming is tussen de broncode en de bytecode; het valideert niet het ontwerp.
"De scanner heeft niets gevonden, dus er zijn geen fouten." Tools zijn het sterkst in het herkennen van bekende patronen, terwijl gebreken op het gebied van economie en contractuele relaties vaak menselijke analyse vereisen.
“Het project heeft een auditrapport, dus de huidige implementatie is gedekt.” Vergelijk de commits, de reikwijdte, de implementatieadressen, de oplossingen en de upgradegeschiedenis van het rapport.
“Fuzz-tests zijn geslaagd, dus de invariant is correct.” Controleer eerst of de invariant de beoogde economische eigenschap uitdrukt en of de handlers zinvolle toestanden bereiken.
“Beheerderscontrole is geen beveiligingsprobleem.” Het is wellicht een bewuste aanname van vertrouwen, maar gebruikers moeten wel kunnen zien wie de bevoegdheid heeft om munten te maken, te pauzeren, parameters te wijzigen of upgrades uit te voeren.
Een laatste zelfcontrole voordat je het resultaat vertrouwt.
Je zou de volgende vragen met 'ja' moeten kunnen beantwoorden:
Heb ik de exacte blockchain, het adres, de bytecode, de proxy, de implementatie en de build-instellingen vastgelegd?
Heb ik alle toegangspunten die de status kunnen wijzigen en de activa die daardoor beïnvloed kunnen worden, in kaart gebracht?
Heb ik alles correct gecompileerd, waarschuwingen gecontroleerd en geautomatiseerde bevindingen gesorteerd?
Heb ik elke externe aanroep, callback, low-level aanroep en foutpad getraceerd?
Heb ik afronding, limieten, nulwaarden, verouderde gegevens en herhaalde acties getest?
Heb ik alle bevoorrechte rollen, sleutels, vertragingen, initialisaties en upgradepaden in kaart gebracht?
Heb ik betekenisvolle vage en invariante tegenvoorbeelden behouden?
Kan een onafhankelijke beoordelaar elke bevinding reproduceren en elke oplossing verifiëren?
Als een antwoord 'nee' is, markeer de audit dan als onvolledig en vermeld het ontbrekende bewijsmateriaal. Een transparante beperking is nuttiger dan een vage conclusie als 'veilig'. De beveiliging van smart contracts is een continu proces: elke upgrade, wijziging van afhankelijkheden, nieuwe integratie en wijziging van privileges kan een nieuwe beoordelingsgrens creëren.