Ghid final pentru securitatea contractelor inteligente EOS

Ghid final pentru securitatea contractelor inteligente EOS. Comunitatea criptografică a devenit sceptică când cel mai mare ICO din lume, EOS a fost lansat în iunie 2018 și s-a blocat timp de 2 zile din cauza unei eroare software. Dar avansul rapid de 4 luni și EOS astăzi reprezintă mai mult de dubla tranzacțiile pe care Ethereum o face astăzi. Prin promisiunea de tranzacții gratuite și mai rapide, cel mai înalt Dapp al EOS are aproximativ 13.000 de utilizatori activi zilnic, comparativ cu doar 2.000 din cei mai buni Dapp din Ethereum.

Securitate EOS Smart Contract

De Rohan Agarwal

Unele vulnerabilități generale ale contractelor inteligente sunt aplicabile pentru aproape toate platformele. La fel ca Ethereum, contractele inteligente scrise pe EOS trebuie să fie auditate înainte de a intra în direct pe mainnet. Bugurile fatale din contract pot fi exploatate atunci când contractele nu sunt suficient de testate pentru luptă. În acest ghid, vă vom ajuta să evitați capcanele obișnuite pe drumul dvs. pentru a crea următorul killer dApp pe EOS.

Înainte de a citi ghidul, este important să cunoașteți câteva informații preliminare referitoare la dezvoltarea EOS, care vă vor fi la îndemână în timp ce citiți ghidul. Cunoașterea C ++ este o necesitate. Cel mai bun loc pentru a începe cu dezvoltarea de contracte inteligente este al EOSIO documentație

Tratarea cu Dispeceratul ABI

extern "C" {

se aplică nul (receptor uint64_t, cod uint64_t, acțiune uint64_t) {

class_name acest contract (destinatar);

if ((cod == N (eosio.token)) && (acțiune == N (transfer))) {

acțiune_executare (&acest contract, &nume_clasă :: transfer);

întoarcere;

}

if (cod! = receptor) return;

switch (acțiune) {EOSIO_API (class_name, (action_1) (action_n))};

eosio_exit (0);

}

}

Imaginea de mai sus este un exemplu de cod al unui dispecerat ABI modificat. Un dispecerat ABI mai simplu, așa cum se arată mai jos, este utilizat pentru gestionarea mai simplă a acțiunii contractului.

EOSIO_ABI (class_name, (action_1) (action_n));

Dispeceratul / expeditorul ABI permite contractului să asculte evenimentele de transfer eosio.token, precum și interacțiunile normale cu contractul inteligent. Este important să legați fiecare acțiune cheie și cod pentru a îndeplini cerințele, pentru a evita apelurile anormale și ilegale.

Un exemplu ar fi hack-ul care s-a întâmplat cu dApp Cazinoul EOSBet datorită unei erori în codul sursă de redirecționare ABI.

if (cod == auto || cod == N (eosio.token)) {

TIPAȚI acest contract (auto);

comutare (acțiune) {

EOSIO_API (TIP, MEMBRI)

}

}

Verificarea de mai sus în aplicația de gestionare a acțiunii codului sursă de redirecționare ABI a permis unui atacator să ocolească complet funcția eosio.token :: transfer () și să apeleze direct funcția contract :: transfer () fără a transfera EOS la contract înainte de a plasa pariu. Pentru pierderi, nu i s-a plătit nimic, dar nu a pierdut nimic. Cu toate acestea, pentru câștiguri, a fost plătit EOS real din contract.

Au remediat eroarea de mai sus adăugând o verificare a acțiunii de transfer a contractului eosio.token înainte ca acțiunile primite să solicite contractului.

if (cod == auto || cod == N (eosio.token)) {

if (acțiune == N (transfer)) {

eosio_assert (cod == N (eosio.token), "Trebuie să transferați EOS");

}

TIPAȚI acest contract (auto);

comutare (acțiune) {

EOSIO_API (TIP, MEMBRI)

}

}

Este important să utilizați declarația require_auth (cont); în acțiuni pe care doriți să le execute numai contul autorizat. require_auth (_self); este utilizat pentru a autoriza doar proprietarul contractului să semneze tranzacția

Autorizarea în acțiuni

token nul: transfer (nume_cont de la, nume_cont la, cantitate activ)

{

auto sym = quantity.symbol.name ();

require_recipient (from);

require_recipient (to);

plătitor auto = has_auth (to)? de la;

sub_echilibru (din, cantitate);

add_balance (către, cantitate, plătitor);

}

Exemplul de cod de mai sus permite oricui să apeleze acțiunea. Pentru a o rezolva, utilizați require_auth (de la); declarație pentru a autoriza plătitorul să solicite acțiunea.

Încercați să evitați modificarea contractului eosio.token

Un hacker recent cu pălărie albă a reușit revendică 1 miliard de jetoane a unui dapp din cauza unui apel metodic slab testat în contractul lor eosio.token. Dapp Se7ens (acum inactiv) a declarat o nouă metodă în cadrul contractului eosio.token pentru aruncarea jetoanelor în conturile de utilizator. Contractul nu a solicitat emiterea sau acțiunea de transfer a contractului eosio.token pentru a reflecta modificările și, prin urmare, fondurile au apărut în mod magic în conturile utilizatorilor. În al doilea rând, au uitat să verifice suma din metodă înainte de transfer, ceea ce a permis hackerului să revendice 1 miliard de jetoane în acest proces..

În afară de modificarea ofertei maxime și a simbolului simbolului, este recomandabil să evitați modificarea acestuia pentru funcții personalizate, deoarece erorile din contractul eosio.token pot fi fatale. Pentru a facilita în siguranță un airdrop, transferați jetoanele airdrop într-un cont separat și distribuiți-l de acolo.

Modificarea proprietăților tabelului multi-index

În prezent, EOS stochează date pe o bază de date de memorie partajată pentru partajarea între acțiuni.

struct [[eosio :: table]] persoană {

cheie nume_cont;

std :: string prenume_nume;

std :: string prenume;

std :: string street;

std :: string city;

std :: starea șirului;

uint64_t primary_key () const {cheie return; }

};

typedef eosio :: multi_index<N (oameni), persoană> adresa_index;

Exemplul de cod de mai sus creează un tabel multi_index numit oameni care se bazează pe structura de date a unui singur rând al acelui tabel folosind persoana struct. EOS în prezent nu permite modificarea a proprietăților tabelului odată ce este implementat. eosio_assert_message afirmarea eșecului va fi eroarea care va fi aruncată. Prin urmare, proprietățile trebuie să fie complet gândite înainte de implementarea tabelului. Altfel, trebuie creată o nouă masă cu un nume diferit și trebuie acordată o atenție deosebită atunci când migrați de la o masă veche la una nouă. Nerespectarea acesteia poate duce la pierderea datelor.

Verificare numerică a depășirii

Atunci când efectuați operațiuni aritmetice, valorile se pot revărsa dacă condițiile limită nu sunt verificate suficient de responsabil, provocând pierderea activelor utilizatorilor.

transfer nul (simbol nume_simbol, nume_cont de la, nume_cont la, sold uint64_t) {

require_auth (de la);

cont din cont;

eosio_assert (is_balance_within_range (balance), "sold nevalid");

eosio_assert (sold > 0, "trebuie să transfere sold pozitiv"); uint64_t sumă = sold * 4; // Debord de multiplicare

}

În exemplul de cod de mai sus, folosind uint64_t pentru a indica echilibrul utilizatorului poate provoca depășire atunci când valoarea se înmulțește. Prin urmare, evitați utilizarea uint64_t pentru a indica solduri și efectuarea de operațiuni aritmetice pe acesta pe cât posibil. Utilizați structura activelor definită în eosiolib pentru operațiuni, mai degrabă decât soldul exact care are grijă de condițiile de depășire.

Având grijă de presupunerile din contract

Vor exista ipoteze care vor necesita afirmații în timpul executării contractului. Utilizarea eosio_assert va avea grijă de condițiile prealabile și va opri executarea acțiunii specifice dacă afirmațiile eșuează. Ca exemplu –

void assert_roll_under (const uint8_t& roll_under) {

eosio_assert (roll_under >= 2 && roll_under <= 96,

"rola sub preaplin, trebuie să fie mai mare de 2 și mai mică de 96");

}

Afirmația afirmă de mai sus presupune că roll_under întreg este mai mare decât 2 & mai puțin de 96. Dar dacă nu, aruncați mesajul de mai sus și opriți execuția. Eșecul descoperirii unor cazuri de colț precum cele de mai sus ar putea deveni catastrofal pentru casa care stabilește regulile.

Generarea de numere aleatoare adevărate

Generarea de numere aleatoare adevărate pe blockchain-ul EOS este încă un risc dacă nu se face cu precizie. Dacă nu faceți acest lucru corect, un adversar prezice rezultatele, jucând întregul sistem în acest proces. Servicii precum Oracalize.it există pentru a furniza numere aleatorii dintr-o sursă externă, dar acestea sunt scumpe și un singur punct de eșec. Oamenii au folosit în trecut variabilele contextuale ale Blockchain (numărul de bloc, ștampila de bloc etc.) pentru a genera numărul aleatoriu în contractul inteligent Ethereum, dar a fost a jucat înainte. Pentru a face generația corect, programul trebuie să ofere un fel de aleatorie combinată pe care nicio parte nu ar putea să o controleze singură. Una dintre cele mai bune modalități posibile în prezent este o metodă sugerată de Dan Larimar însuși atunci când generează un număr aleatoriu între două părți.

BountyOne Blog: EOS Smart Contract Security

șir sha256_to_hex (const checksum256& sha256) {

reveniți la_hex ((char *) sha256.hash, sizeof (sha256.hash));

}

șir sha1_to_hex (const checksum160& sha1) {

reveniți la_hex ((char *) sha1.hash, sizeof (sha1.hash));

}

șablon <clasa T>

Inline void hash_combine (std :: size_t& sămânță, const T& v) {

std :: hash<T> hasher;

semințe ^ = hasher (v) + 0x9e3779b9 + (semințe << 6) + (sămânță >> 2);

}

Codul eșantion de mai sus oferă o generație optimizată de numere aleatoare între 1 și 100. seed1 este semința house și seed2 este seedul utilizatorului de mai sus. Pentru trimitere, Dappub și EOSBetCasino și-au obținut contractele complete cu implementarea aleatorie a unui număr de zaruri între jucător și casă (dezvoltator).

uint8_t compute_random_roll (const checksum256& seed1, const checksum160& sămânță2) {

size_t hash = 0;

hash_combine (hash, sha256_to_hex (seed1));

hash_combine (hash, sha1_to_hex (seed2));

returnează hash% 100 + 1;

}

EOSBet a primit recent piratat din nou de 65.000 de EOS când un adversar și-a păcălit contractul eosio.token pentru a trimite EOS la portofelul său ori de câte ori tranzacționa între propriile sale portofele. Codul contractului eosio.token notifică atât expeditorul, cât și receptorul de jetoane EOS care există jetoane primite. Pentru a imita comportamentul & facilitează hack-ul, adversarul a creat două conturi, să presupunem că A & B. A a avut un contract inteligent cu o acțiune cu declarație require_recipient (N (eosbetdice11)). Când A a facilitat tranzacția de la A la B prin apelul de acțiune, a notificat funcție de transfer în contract de parcă apelul ar fi venit din contractul eosio.token. Deoarece nu a existat un transfer real al EOS în contract, ori de câte ori hackerul a pierdut un pariu, acesta nu a pierdut nimic, dar a fost recompensat când a câștigat pariul. Prin urmare, verificarea numai a numelui contractului și a acțiunii nu este suficientă.

Verificări privind notificările din contracte

Pentru a atenua problema, funcția ar trebui să verifice dacă contractul este într-adevăr receptorul jetoanelor sau nu.

eosio_assert (transfer_data.from == _self || transfer_data.to == _self, "Trebuie să fie transferul de intrare sau de ieșire");

Care sunt cele mai bune practici pe care ar trebui să le urmați în timp ce dezvoltați un contract inteligent pe EOS?

Bug-urile sunt o parte inevitabilă a oricărui software. Consecințele sale se amplifică într-un mediu descentralizat, mai ales dacă implică tranzacții de valoare. În afară de garanțiile specifice EOS discutate mai sus, iată câteva dintre precauțiile generale și cele mai bune practici pe care ar trebui să le aibă în vedere noii dezvoltatori de contracte inteligente –

  1. Mereu audit contractul independent de firmele terțe de audit inteligent al contractelor înainte de lansarea pe mainnet.
  2. Efectuați depanarea necesară Caveman (singura modalitate de depanare a contractului în prezent) a contractului înainte de a elibera pe testnet. Documentația EOSIO are o mare ghid pentru aceasta.
  3. Setați rata limită de transfer la retrageri pentru a evita pierderile excesive în primele zile de lansare a rețelei principale Aveți un program de recompensă pentru erori pentru dezvăluirea responsabilă de către hackeri de pălărie albă.
  4. Aveți un comutator care să înghețe contractul atunci când este detectat un bug.

Pentru a-l implementa, persistăm un flag în tabelul multi_index. Stabilim steagul folosind o acțiune care poate fi apelată numai de către proprietarul contractului. Și apoi verificăm la fiecare acțiune publică dacă steagul este setat să fie înghețat sau nu. Un exemplu de implementare a funcției este dat mai jos.

struct st_frozen {

uint64_t înghețat;

};

typedef singleton<N (îngheț), st_frozen> tb_frozen;

tb_frozen _frozen;

uint64_t getFreezeFlag () {

st_frozen frozen_st {.frozen = 0};

returnează _frozen.get_or_create (_self, frozen_st);

}

void setFreezeFlag (const uint64_t& pFrozen) {

st_frozen frozen_st = getFreezeFlag ();

congelat_st.frozen = pFrozen;

_frozen.set (frozen_st, _self);

}

// Acțiune publică

void freeze () {

require_auth (_self);

setFreezeFlag (1);

}

// Acțiune publică

void unfreeze () {

require_auth (_self);

setFreezeFlag (0);

}

// orice acțiune publică

acțiune nulă (…) {

eosio_assert (getFreezeFlag (). congelat == 1, "Contractul este blocat!");

}

  1. Fiți la curent cu privire la îmbunătățirile de securitate din biblioteci sau despre dezvăluirile de vulnerabilități de pe platformă. Actualizați imediat bibliotecile atunci când este necesar.
  2. Open source codul contractului cel puțin, astfel încât echitatea să fie menținută în joc și dezvoltatorii independenți ar putea ajuta la detectarea erorilor mult mai repede.

EOS Smart Contract Security: Concluzie

Au trecut doar 5 luni de la lansarea EOS, dar a depășit așteptările. Compensările pe care le-a făcut – DPOS, contracte inteligente mutabile, 21 de noduri miniere etc. s-au confruntat cu siguranță cu critici grele din partea maximalistilor de descentralizare. Cu toate acestea, nu a oprit dApp-urile bazate pe Ethereum să treacă la EOS, având în vedere scalabilitatea pe care platforma le oferă astăzi. Fie că EOS sau Ethereum câștigă războiul este încă de decis, dar EOS a câștigat cu siguranță bătălia. Și va rămâne același până când Ethereum va reuși să atingă scalabilitatea de care are nevoie lumea pentru a rula „The World Computer”.

_________________________________________________________________________________________

Acest articol a fost scris de Rohan Agarwal

Bio – #Android Dev # Antreprenor # Blockchain Dev & Co-fondator cercetător @ Cypherock.com – Un portofel hardware securizat pentru smartphone-uri.

Linkedin – https://www.linkedin.com/in/rohanagarwal94/

Github – https://github.com/rohanagarwal94

Twitter – https://twitter.com/rohanagarwal94

Mike Owergreen Administrator
Sorry! The Author has not filled his profile.
follow me