S novým článkom do blogu som si dal dosť načas. Malo to svoje dôvody a práve jeden z tých dôvodov bol audit smart contractu, pre ktorý ma oslovil jeden nemenovaný projekt (nemenovaný lebo NDA). Napadlo mi, že toto môže byť skvelá téma aj pre nový príspevok do blogu, kde objasním ako sa pripraviť na audit a ako audit prebieha. Je veľmi náročné popísať samotný audit a čím všetkým musí auditor prejsť, no posnažím sa dať aspoň čiastočnú predstavu o postupoch a základných bezpečnostných chybách pri písaní smart contractov na sieti Ethereum.

Každý, kto plánuje spustiť projekt na sieti Ethereum alebo plánuje ICO, si musí uvedomiť dôležitosť auditu smart contractov. Externý audit zabezpečí správnosť funkcionalít vášho smart contractu a ochráni vás od slabín vášho vlastného kódu. Audit smart contractov je veľmi citlivá vec, na ktorú je potrebné patrične pripraviť nielen interný tím ale aj kód, čím sa zabezpečí hladký a prehľadný priebeh celého auditu.

 

Ako sa pripraviť na audit?

1. Dokumentácia je alfou a omegou

Vývoj smart contractov sa častokrát prirovnáva k tak citlivej záležitosti, ako je vývoj softwaru pre NASA. Nielen, že musí byť tak bezchybný, keďže máte len jediný pokus, ale rovnako musí byť tak aj zdokumentovaný.

Špecifikácia kódu by mala byť písaná v anglickom jazyku s popisom každej funkcie v kóde, ako pracujú, aké výstupy sa očakávajú a ako tieto funkcie pracujú medzi sebou. Efektívnym popisom funkcií môže byť aj forma “black box”, kde špecifikácia popisuje čo vchádza do funkcie, aké parametre vyžaduje, čo by sa malo a čo by sa nemalo stať vo vnútri funkcie a čo funkcia vracia na výstupe.

margaret_hamilton
Neodpustím si foto Margaret Hamiltonovej stojacej vedľa dokumentácie zdrojového kódu Apollo Guidance Computer, ktorý dostal ľudstvo na mesiac a ktorého je autorom.

Programátori častokrát využívajú túto metódu dokumentácie už pri samotnom písaní kódu. Pomôže to zamyslieť sa hlbšie nad samotnou funkcionalitou či možnými alternatívami.

Skvelým príkladom môže byť aj Golem.

2. Dokumentácia procesu od začiatku až po koniec

Dokumentácia smart contractor by mala obsahovať aj presnú špecifikáciu krokov od samotného momentu prípravy na deploy – verzia compileru, definícia inicializačných hodnôt v smart contracte či hodnoty parametrov v konštruktore, cez presný proces ovládania smart contractu až po jeho zánik (moment, keď je SC už useles) posledným krokom. Niečo ako:

1. Deploy SC (initial params: xxx, 0x0xxx)
2. Total supply should be 1100000
3. Tokens allocated for bonus – 1000000
4. Start by function “startICO()”
atď…

3. Správne okomentovaný kód a zapnutý linter v editore

Komentáre k funkciám sú základ, ale komentáre ku komplexným častiam kódu sú veľkou pomôckou pre auditora, ktorý nemusí študovať riadok po riadku a dedukovať, čo asi táto časť kódu vykonáva. Zostáva pravidlom, že čím komplexnejší kód, tým viac priestoru na problémy. Pre držanie sa správnej syntaxe a vyhnutiu sa obrazne povedané “povrchových” problémov je základom používať vo svojom editore linter pre solidity.

4. Dobré README nie je na škodu

Projekt, resp. zložka so zdrojovými kódmi by mal obsahovať súbor README.md, ktorý zrozumitelne vysvetľuje projekt, určenie a cieľ smart contractov. Netreba zabúdať na popis inštalácie a postup ich sputenia či testovania s pripojenými odkazmi na použitý už zauditovaný kód napr. od Zeppelin Solidity.

5. Uprataný repozitár

Okrem README, ktoré zasvieti na auditora hneď po otvorení repozitára, by mal byť celý projekt uprataný a uložený v špecificky pomenovaných zložkách, so špecificky pomenovanými súbormi. Malo by byť okamžite jasné, ktoré súbory boli použité pre testovacie účely a ktoré smart contracty budú použité vo verzii na Mainnete a sú určené na testovanie a audit. Repozitár by nemal obsahovať žiadne poznámky určené len pre osobné použitie autora či žiadne rozrobené a zbytočné súbory, ktoré nemajú s auditom a finálnym kódom nič spoločné.

Štruktúra celého repozitáru sa zvyčajne drží štruktúry využívanej v rámci frameworku Truffle. Zložka “contracts” je určená len pre smart contracty. Vnorená zložka “base” (“contract/base”) by mala obsahovať už vopred zauditované contracty, napr. contracty využité z knižníc a repozitára OpenSolidity. Zložka “test” by mala obsahovať automatické testy napísané pre smart contracty.

6. Posledný commit určuje verziu auditovaného kódu.

Po odovzdaní zdrojového kódu na audit je dôležité vykonať posledný commit, ktorý bude určovať aktuálnu, poslednú verziu auditovaného kódu. Posledný commit by mal byť vždy commit autora zdrojového kódu a číslo, prípadne iný identifikátor tohto commitu by mal byť pripojený v záverečnej správe, potvrdení alebo certifikáte o audite. Pokiaľ je nutné po odovzdaní zdrojového kódu ešte niečo commitovať do repozitára, je nutné a extrémne dôležité o tom auditora informovať.

Dobrý audit môže priniesť pokoj v duši vašim programátorom, ale treba myslieť na to, že nezbavuje vás ani vašich programátorov zodpovednosti. Dobrou prípravou na audit ale uľahčíte prácu auditorovi a umožníte mu vykonať svoju úlohu najlepšie ako dokáže.

 

Samotný audit

Prvé kolo auditu – pripomienkovanie funkcionalít a statická analýza

Ja osobne som nezačal okamžite pracovať so zdrojovým kódom ale začal som pripomienkovaním “good and bad practises” v rámci ICO stratégie a distribúcie tokenov investorom, čo som patrične zdokumentoval s odporúčaniami na nápravu a príkladmi riešenia.

Až neskôr som začal so samotnou statickou analýzou zdrojového kódu, t.j. štruktúra a úhľadnosť kódu ako takého, štruktúra použitých smart contractov a tým štruktúra dedenia funkcionalít a nastavenie inicializačných parametrov pri konštruktoroch. V tejto časti môžem len odporučiť využívať zdrojové kódy od Zeppelin Solidity. Sú už vopred zauditované, udržiavané komunitou a pokrývajú 80% potrieb ICO stratégií. Pri použití Zeppelin Solidity je ale potrebné držať sa dedenia funkcionality z odporúčanej štruktúry uvedenej v dokumentácii. Je potrebné myslieť na to, že váš kód prezentuje nielen vás ako autora ale zároveň aj spoločnosť, preto je potrebné aj jeho štruktúru držať uhladenú. Nikdy nemiešame funkcionalitu tokenu s funkcionalitou ovládaniaICO smart contractov a bonusovej schémy. Príklad:
library SafeMath {…}
contract Ownable {…}
contract ERC223Interface {…}
contract YourTokenERC223 is ERC223Interface {…}
contract OwnableToken is YourTokenERC223, Ownable {…}
YourToken is OwnableToken {…}
contract Crowdsale {…}
contract FinalizableCrowdsale is Crowdsale, Ownable {…}
contractYourTokenICO is FinalizableCrowdsale {…}

Druhé kolo auditu – detailnejší check zdrojového kódu

Zdrojový kód sa rozdelí na funkcionalitu, ktorá sa dedí z predtestovaných contractov ako Zeppelin Solidity a kód, ktorý je napísaný programátorom ako špecifická funkcionalita. Zo skúsenosti ako prvé testujem prijímanie etheru na smart contract a s tým spätú bonusovú schému, prepočet bonusových tokenov a ich distribúciu investorom. VŽDY odporúčam a VŽDY budem odporúčať mať podmienené štádiá bonusovej schémy na počet predaných tokenov alebo vyzbierané množstvo kryptomeny. NIKDY nie na čas ako napríklad – prvý týždeň 50%, druhý týždeň 40%. Nejedná sa o bezpečnostnú chybu v kóde ale o logickú biznisovú chybu.

Bonusová schéma či funkcie zabezpečujúce check validity prijatých etherov, je najčastejším miestom pre “over a under flow” alebo inak povedané, pretečenie dátového typu. Príkladom nám môže byť pokus o priradenie hodnoty do premennej dátového typu uint256. Pokiaľ jej priradíme hodnotu vyššiu ako 2**256, priradená hodnota bude nula. V prípade “under flow” sa stane to isté, pokiaľ sa pokúšame odčítať od nuly hodnotu vyššiu ako nula. Pre tento prípad je kľúčové využívať knižnicu SafeMath od Open Zeppelin, ktorá minimalizuje riziko pri takýchto matematických operáciách.

Pokiaľ smart contract vykonáva to, čo je v dokumentácii popísané, nasleduje detailnejší check modifierov a podmienok v kóde. Správne nastavené “modifiers” pre určovanie práv ovládania funkcií “ownable” sú taktiež alfa a omega bezpečnosti, keďže nechceme aby nám ktokoľvek prevzal kontrolu nad vlastným smart contractom.

Ďalším bodom, ktorý stojí za zmienku je správne využívanie funkcií assert() a require() na miestach, kde je to potrebné. Obidve funkcie sa správajú takmer identicky, s tým rozdielom, že assert() sa využíva na validovanie hodnôt contractu až po tom čo boli zmeny vykonané, zatiaľ čo require() je využívaný ako podmienka ešte pred samotným vykonaním funkcií pre verifikáciu vstupných hodnôt funkcií ako napríklad overenie či transakcia neprichádza z nulovej 0x0 adresy.

Nepríde mi moc efektívne popisovať všetky možné slabiny a snažiť sa všetko z dokumentácií prekladať do slovenčiny, preto určite odporúčam Solidity znalejším, pre zaujímavosť pozrieť tieto zdroje:

HackPedia: 16 Solidity Hacks/Vulnerabilities, their Fixes and Real World Examples

Summary of the Common Smart Contract Vulnerabilities

The ERC20 Short Address Attack Explained

Audit of Deployed Smart Contract

Posledné štádium auditu je samotná simulácia ICO a testovanie náchylnosti na chybovosť či zraniteľnosť počas samotného procesu. Smart contracty deployneme na preferovanú sieť – najčastejšie využívam Ropsten a až v poslednom checku deployujem na Mainnet a prechádzam každú funkciu presne podľa dokumentácie. Napríklad, pokiaľ začiatok ICO definovaný v smart contracte je určený na neskôr ako ihneď po tom, čo prebehne deploy, smart contract by nemal vedieť prijímať žiadne prostriedky od investorov, prípadne by investor vôbec nemal byť schopný vygenerovať transakciu a zbytočne míňať gas.

Takto podľa krokov sa zostaví tabuľka kde sa zaznamená krok alebo vykonaný “útok”, zaznamená sa adresa odkiaľ bola transakcia vykonaná, adresa deploynutého smart contractu na sieti a link na TX transakcie na Etherscane. Takýmto spôsobom sa viem vždy spätne vrátiť k deploynutému contractu na sieť a skontrolovať stav smart contractu v čase testu.

Okrem tejto tabuľky sa podľa potreby a nájdených slabín v zdrojom kóde vytvoria zoznamy pre nájdené problémy zoradené podľa:

1. Critical vulnerabilities
Kritické bezpečnostné diery nájdené v smart contracte

2. Medium vulnerabilities
Niektoré funkcie napr. nie sú označené ako pure alebo view

3. Low severity vulnerabilities and recommendations
Zle použité assert() alebo require()

Spolu so sumárom auditu by mal odovzdávaný dokument o výsledku auditu obsahovať aj “Disclaimer” o tom, že tento dokument nie je právne záväzným dokumentom a tým pádom negarantuje nič. Dokument je určený len pre účely diskusie.

 

Záver

Audit smart contractov je nesmierne obšírna téma a podľa toho, čo som si všimol pri iných projektoch, každý audit prebieha inak a každý auditor má svoje vlastné techniky a postupy. Zámerne som nespomenul všetko, čo taký príspevok o audite by mal obsahovať, keďže je toho doslova na niekoľko hodín čítania, štúdia a testovania na “koži vlastného smart contractu”, no verím, že tento príspevok pomôže načrtnúť o čom vlastne audit smart contractov je a najmä upozorní na jeho dôležitosť, keďže možno naše tokeny sú v čase ICO iba fazuľky, no treba mať na pamäti, že náš zdrojový kód operuje s obrovskou čiastkou peňazí investorov, ktorí nám dôverujú.

1