Varnostna priporočila pametne pogodbe Ethereum

blog 1NewsDevelopersEnterpriseBlockchain ExplainedDogodki in konferencePressGlasila

Naročite se na naše novice.

Email naslov

Spoštujemo vašo zasebnost

DomovBlogBlockchain razvoj

Varnostna priporočila pametne pogodbe Ethereum

Tukaj je 10+ varnostnih vzorcev pametnih pogodb, ki jih je treba upoštevati pri gradnji na Ethereum.by ConsenSys 10. julij 2020 Objavljeno 10. julija 2020

Varnostna priporočila pametne pogodbe Ethereum

Kot smo zajeli v Varnostnem naboru pametnih pogodb, je budni razvijalec Ethereuma vedno v ospredju pet načel:

  • Pripravite se na neuspeh
  • Previdno uvedite
  • Naj bodo pogodbe enostavne
  • Bodite na tekočem
  • Zavedajte se posebnosti EVM

V tej objavi se bomo poglobili v idiosinkrazije EVM in se sprehodili po seznamu vzorcev, ki jih morate upoštevati pri razvoju katerega koli sistema pametnih pogodb na Ethereumu. Ta del je namenjen predvsem razvijalcem Ethereum. Če ste še vedno v zgodnjih fazah raziskovanja, si oglejte program razvijalcev blokov verige ConsenSys Academy. 

V redu, potopimo se.

Zunanji klici

Pri zunanjih klicih bodite previdni

Klici na nezaupanja vredne pametne pogodbe lahko povzročijo več nepričakovanih tveganj ali napak. Zunanji klici lahko izvajajo zlonamerno kodo v tej pogodbi ali kateri koli drugi pogodbi, od katere je odvisna. Zato vsak zunanji klic obravnavajte kot potencialno varnostno tveganje. Če zunanjih klicev ni mogoče odstraniti ali jih ni zaželeno, uporabite priporočila v nadaljevanju tega oddelka, da zmanjšate nevarnost.

Označi nezaupne pogodbe

Pri interakciji z zunanjimi pogodbami poimenujte spremenljivke, metode in vmesnike pogodb tako, da je jasno, da interakcija z njimi potencialno ni varna. To velja za vaše funkcije, ki kličejo zunanje pogodbe.

// slabo Bank.withdraw (100); // Nejasno, ali zaupanja vredna ali nezaupna funkcija makeWithdrawal (uint znesek) {// Ni jasno, da je ta funkcija potencialno nevarna Bank.withdraw (znesek); } // dobra UntrustedBank.withdraw (100); // nezaupljiv zunanji klic TrustedBank.withdraw (100); // zunanja, a zaupanja vredna bančna pogodba, ki jo vzdržuje funkcija XYZ Corp makeUntrustedWithdrawal (uint znesek) {UntrustedBank.withdraw (znesek); } Jezik kode: PHP (php)

Izogibajte se spremembam stanja po zunanjih klicih

Ne glede na to, ali uporabljate neobdelane klice (v obliki someAddress.call ()) ali pogodbene klice (v obliki ExternalContract.someMethod ()), predpostavimo, da se lahko izvaja zlonamerna koda. Tudi če ExternalContract ni zlonamerna, se lahko zlonamerna koda izvede s katero koli pogodbo, ki jo pokliče.

Ena posebna nevarnost je zlonamerna koda, ki lahko ugrabi nadzorni tok in vodi do ranljivosti zaradi ponovnega vstopa. (Glej Ponovno vstop za popolnejšo razpravo o tej težavi).

Če kličete nezaupljivo zunanjo pogodbo, se po klicu izogibajte spremembam stanja. Ta vzorec se včasih imenuje tudi vzorec preverjanja-učinki-interakcije.

Glej SWC-107

Ne uporabljajte prenosa () ali pošiljanja ().

.transfer () in.send () posreduje natanko 2.300 plina prejemniku. Cilj tega trdo kodiranega plačila za plin je bil preprečiti ranljivosti ponovnega vstopa, vendar je to smiselno le ob predpostavki, da so stroški plina konstantni. EIP 1884, ki je bil del istanbulskih trdih vilic, je povečal stroške plina pri operaciji SLOAD. To je povzročilo, da je pogodbena nadomestna funkcija stala več kot 2300 plina. Priporočamo, da prenehate uporabljati.transfer () in.send () in namesto tega uporabite.call ().

// slaba pogodba Ranljiva {funkcija dvig (uint256 znesek) zunanja {// To posreduje 2300 plina, kar morda ne bo dovolj, če je prejemnik // pogodba in se stroški plina spremenijo. msg.sender.transfer (znesek); }} // dobra pogodba Fiksna {odvzem funkcije (uint256 znesek) zunanji {// Ta posreduje ves razpoložljivi plin. Preverite povratno vrednost! (uspeh bool,) = msg.sender.call.value (znesek) (""); zahtevajo (uspeh, "Prenos ni uspel."); }} Jezik kode: JavaScript (javascript)

Upoštevajte, da.call () ne pomaga ublažiti ponovnih napadov, zato je treba upoštevati druge previdnostne ukrepe. Če želite preprečiti ponovne napade, uporabite vzorec preverjanja-učinki-interakcije.

Obravnavajte napake v zunanjih klicih

Solidity ponuja klicne metode na nizki ravni, ki delujejo na surovih naslovih: address.call (), address.callcode (), address.delegatecall () in address.send (). Te metode na nizki ravni nikoli ne vržejo izjeme, ampak vrnejo false, če klic naleti na izjemo. Po drugi strani pa bodo pogodbeni klici (npr. ExternalContract.doSomething ()) samodejno razširili met (na primer ExternalContract.doSomething () bo vrgel tudi, če vrne doSomething ()).

Če se odločite za uporabo nizkokakovostnih klicnih metod, poskrbite, da boste obvladali možnost, da klic ne bo uspel, tako da preverite vrnjeno vrednost.

// slabo someAddress.send (55); someAddress.call.value (55) (""); // to je dvakrat nevarno, saj bo posredoval ves preostali plin in ne bo preverjal rezultata someAddress.call.value (100) (bytes4 (sha3 ("depozit ()"))); // če depozit vrne izjemo, bo neobdelani klic () vrnil samo false in transakcija NE bo vrnjena // dobra (uspeh bool,) = someAddress.call.value (55) (""); if (! success) {// handle code code} ExternalContract (someAddress) .deposit.value (100) (); Jezik kode: JavaScript (javascript)

Glej SWC-104

Za zunanje klice ugodnejši pull-over push

Zunanji klici lahko nehajo naključno ali namerno. Da bi čim bolj zmanjšali škodo, ki jo povzročijo takšne napake, je pogosto bolje, da vsak zunanji klic izoliramo v svojo transakcijo, ki jo lahko sproži prejemnik klica. To je še posebej pomembno pri plačilih, kjer je bolje uporabnikom dovoliti, da dvignejo sredstva, namesto da jim sredstva samodejno pošiljajo. (To tudi zmanjšuje možnost za težave z omejitvijo plina.) Izogibajte se kombiniranju več prenosov etra v eni transakciji.

// dražba s slabo pogodbo {naslov maximumBidder; uint najvišja ponudba; ponudba za funkcijo () plačljivo {zahteva (msg.value >= najvišja ponudba); če (najvišja ponudba! = naslov (0)) {(uspeh bool,) = najvišja ponudba.call.value (najvišja ponudba) (""); zahtevati (uspeh); // če ta klic nenehno ne uspe, nihče drug ne more oddati ponudb} najvišji ponudnik = msg.sender; najvišja ponudba = msg.value; }} // dobra pogodba na dražbi {naslov najvišji ponudnik; uint najvišja ponudba; preslikava (naslov => uint) vračila; funkcija ponudba () plačljivo zunanje {zahteva (msg.value >= najvišja ponudba); če (najvišja ponudba! = naslov (0)) {vračila [najvišja ponudba] + = najvišja ponudba; // evidentiramo vračilo, ki ga lahko ta uporabnik zahteva} najvišji ponudnik = sporočilo pošiljatelja; najvišja ponudba = msg.value; } funkcija povleciteRefund () zunanje {povračilo uint = vračilo [msg.sender]; vračila [msg.sender] = 0; (uspeh bool,) = msg.sender.call.value (vračilo) (""); zahtevati (uspeh); }} Jezik kode: JavaScript (javascript)

Glej SWC-128

Ne delegirajte klica na nezaupljivo kodo

Funkcija delegatecall prikliče funkcije iz drugih pogodb, kot da pripadajo pogodbi o klicatelju. Tako lahko klicatelj spremeni stanje klicnega naslova. To je lahko negotovo. Spodnji primer prikazuje, kako lahko uporaba klica delegate privede do uničenja pogodbe in izgube njenega stanja.

pogodba Destructor {funkcija doWork () external {selfdestruct (0); }} pogodbeni delavec {funkcija doWork (naslov _internalWorker) public {// unsafe _internalWorker.delegatecall (bytes4 (keccak256 ("naredi delo()"))); }} Jezik kode: JavaScript (javascript)

Če se Worker.doWork () kot argument pokliče z naslovom razporejene pogodbe o destruktorju, se pogodba Worker samodejno uniči. Pooblastilo za izvršitev zaupajte samo pogodbam in nikoli na naslov uporabnika.

Opozorilo

Ne domnevajte, da so pogodbe ustvarjene z ničelnim stanjem. Napadalec lahko eter pošlje na naslov pogodbe, preden je ustvarjena. Pogodbe ne smejo predvidevati, da prvotno stanje vsebuje ničelno stanje. Glej številka 61 za več podrobnosti.

Glej SWC-112

Ne pozabite, da je eter mogoče prisilno poslati na račun

Pazite se kodiranja invariante, ki strogo preverja ravnotežje pogodbe.

Napadalec lahko prisilno pošlje eter na kateri koli račun. Tega ni mogoče preprečiti (niti s pomožno funkcijo, ki izvede razveljavitev ()).

Napadalec lahko to stori tako, da ustvari pogodbo, jo financira z 1 wei in se sklicuje na samodejno uničenje (AddressAddress). V programu AddressAddress ni priklicana nobena koda, zato je ni mogoče preprečiti. To velja tudi za blokovno nagrado, ki se pošlje na naslov rudarja, ki je lahko poljuben poljuben naslov.

Ker je mogoče naslove pogodbe predhodno izračunati, lahko eter pošljemo na naslov pred uvedbo pogodbe.

Glej SWC-132

Ne pozabite, da so podatki v verigi javni

Številne prijave zahtevajo, da so predloženi podatki do nekega trenutka zasebni, da lahko delujejo. Igre (npr. Škarje za papir v obliki verige) in dražbeni mehanizmi (npr. Zapečatena ponudba Dražbe Vickrey) sta dve glavni kategoriji primerov. Če ustvarjate aplikacijo, pri kateri je težava z zasebnostjo, se prepričajte, da se uporabniki izogibajo prezgodnji objavi informacij. Najboljša strategija je uporaba sheme zavez z ločenimi fazami: najprej se zavežite z uporabo zgoščene vrednosti, v kasnejši fazi pa razkrijete vrednosti.

Primeri:

  • Pri škarjah za papir iz papirja zahtevajte, da oba igralca najprej predložita zgoščenost nameravane poteze, nato pa oba igralca, da predložita svojo potezo; če se predložena poteza ne ujema s hashom, jo ​​vrzite ven.
  • Na dražbi od igralcev zahtevajte, da v začetni fazi predložijo zgoščeno vrednost svoje ponudbe (skupaj z depozitom, večjo od njihove ponudbene vrednosti), nato pa v drugi fazi predložijo svojo ponudbeno vrednost na dražbi..
  • Pri razvoju aplikacije, ki je odvisna od generatorja naključnih števil, mora biti vrstni red vedno (1) igralci oddajo poteze, (2) generirano naključno število, (3) igralci izplačajo. Mnogi ljudje aktivno raziskujejo generatorje naključnih števil; trenutne najboljše rešitve v svojem razredu vključujejo glave blokov Bitcoin (preverjene s pomočjo http://btcrelay.org), sheme razpršitve-objave-razkritja (tj. ena stranka ustvari številko, objavi svojo razpršitev, da se “zaveže” vrednosti in nato vrednost razkrije pozneje) in RANDAO. Ker je Ethereum deterministični protokol, v njem ne morete uporabiti nobene spremenljivke kot nepredvidljivo naključno število. Upoštevajte tudi, da rudarji v določeni meri nadzorujejo vrednost block.blockhash ()*.

Pazite se možnosti, da se nekateri udeleženci “spustijo brez povezave” in se ne vrnejo

Postopki vračila kupnine ali zahtevkov ne smejo biti odvisni od določene stranke, ki izvaja določeno dejanje, brez drugih načinov pridobivanja sredstev. Na primer, pri igri škarje papir-papir-škarje je ena najpogostejših napak, da ne izplačate, dokler oba igralca ne predložita svojih potez; zlonamerni igralec pa lahko drugega “žalosti”, tako da preprosto nikoli ne predloži svoje poteze – v resnici, če igralec vidi odkrito potezo drugega igralca in ugotovi, da je izgubil, sploh nima razloga, da bi predložil svojo potezo. To vprašanje se lahko pojavi tudi v okviru poravnave državnih kanalov. Kadar so takšne situacije težavna, (1) zagotovite način izogibanja nesodelujočim udeležencem, morda s časovno omejitvijo, in (2) razmislite o dodajanju dodatne ekonomske spodbude udeležencem, da predložijo informacije v vseh situacijah, v katerih so naj bi to storila.

Pazite se negacije najbolj negativnega podpisanega celega števila

Solidity ponuja več vrst za delo s podpisanimi celimi števili. Kot v večini programskih jezikov lahko tudi v Solidity podpisano celo število z N bitov predstavlja vrednosti od -2 ^ (N-1) do 2 ^ (N-1) -1. To pomeni, da za MIN_INT ni pozitivnega ekvivalenta. Negacija se izvaja kot iskanje komplementa dveh števil, torej negacija najbolj negativnega števila bo povzročilo enako število. To velja za vse podpisane celoštevilčne vrste v Solidity (int8, int16,…, int256).

negacija pogodbe {funkcija negate8 (int8 _i) javni čisti donos (int8) {return -_i; } funkcija negate16 (int16 _i) javno čisti vrnitve (int16) {return -_i; } int8 public a = negate8 (-128); // -128 int16 javno b = negate16 (-128); // 128 int16 javno c = negate16 (-32768); // -32768} Jezik kode: PHP (php)

Eden od načinov za to je preverjanje vrednosti spremenljivke pred negacijo in vrnitev, če je enaka MIN_INT. Druga možnost je zagotoviti, da najbolj negativno število nikoli ne bo doseženo z uporabo tipa z večjo zmogljivostjo (npr. Int32 namesto int16).

Podobna težava z vrstami int se pojavi, če MIN_INT pomnožimo ali delimo z -1.

Ali je vaša koda blockchain varna? 

Upamo, da so bila ta priporočila koristna. Če se vi in ​​vaša ekipa pripravljate na zagon ali celo na začetku razvojnega življenjskega cikla in potrebujete pametne pogodbe, ki so pregledane, vas prosimo, da se obrnete na našo ekipo varnostnih inženirjev pri ConsenSys Diligence. Tu smo, da vam s 100% zaupanjem pomagamo pri zagonu in vzdrževanju aplikacij Ethereum. 

Rezervirajte varnostno preverjanje

Rezervirajte enodnevni pregled z našo ekipo strokovnjakov za varnost veriženja blokov. Rezervirajte svoje danes Varnost Pametne pogodbe Novice Naročite se na naše glasilo, kjer boste prejemali najnovejše novice o Ethereumu, rešitve za podjetja, vire za razvijalce in še več.Kako zgraditi uspešen izdelek BlockchainSpletni seminar

Kako zgraditi uspešen izdelek Blockchain

Kako nastaviti in zagnati vozlišče EthereumSpletni seminar

Kako nastaviti in zagnati vozlišče Ethereum

Kako zgraditi lasten API za EthereumSpletni seminar

Kako zgraditi lasten API za Ethereum

Kako ustvariti socialni žetonSpletni seminar

Kako ustvariti socialni žeton

Uporaba varnostnih orodij pri razvoju pametnih pogodbSpletni seminar

Uporaba varnostnih orodij pri razvoju pametnih pogodb

Prihodnost financ Digitalna sredstva in DeFiSpletni seminar

Prihodnost financ: digitalna sredstva in DeFi

Mike Owergreen Administrator
Sorry! The Author has not filled his profile.
follow me
Like this post? Please share to your friends:
map