Tocmai am văzut cum o mică lacună duce la o pierdere financiară (de mărime variabilă), în același mod în care contractele inteligente dezvoltate în cadrul Solidity sunt predispuse la diverse atacuri cunoscute și necunoscute. Exploatatorii profită de bug-uri și lacune pentru a privi în contracte inteligente și a le manipula pentru a efectua atacuri. Aici vă prezentăm o listă cuprinzătoare a primelor 5 erori frecvent întâlnite în limbajul de programare Solidity.
La QuillAudits urmărim metodologia adaptivă pentru a obține esența fiecărui hack și implementăm învățăturile sale despre viitoarele contracte inteligente pentru a evita orice potențială amenințare.
Erori în limbajul de programare soliditate
1. Apel extern nebifat
Tragem această problemă în primul rând, deoarece este una dintre capcanele Solidity cele mai frecvent observate. În general, trimiterea eterului către orice cont extern se efectuează prin intermediul transfer() funcţie. În afară de aceasta, cele două funcții cele mai utilizate pentru a efectua un apel extern sunt; apel(), și trimite(), aici în principal apel() funcția este utilizată pe scară largă pentru a efectua apeluri externe versatile de către dezvoltatori.
Deși apel() și trimite() funcțiile returnează o valoare booleană specificând dacă apelul a avut succes sau nu. Astfel, în acest caz, dacă există una dintre funcții apel() or trimite() nu reușește să îndeplinească sarcina, vor reveni cu un fals. Prin urmare, dacă dezvoltatorul nu verifică încrucișat valoarea returnată, ar deveni o capcană.
Vulnerabilitatea
Luați în considerare exemplul de mai jos:
contract Lotto {
boolpublic payedOut = false;
adresa câștigătorului public;
uintpublic winAmount;
// ... funcționalitate suplimentară aici
funcția sendToWinner () public {
require (! payedOut);
winner.send (winAmount);
payedOut = adevărat;
}
funcție retireLeftOver () public {
require (payedOut);
msg.sender.send (acest.echilibru);
}
}
În contractul de tip Lotto de mai sus, putem observa că a câştigător primește winAmount de eter lăsând puțin restul pentru a fi retras de la orice agent extern.
Aici, capcana contractului există la linia [11], unde a trimite este utilizat fără validarea încrucișată a răspunsului. În exemplul de mai sus, a câştigător a cărei tranzacție eșuează (fie prin deficiența de gaz, fie dacă este un contract care intră în mod intenționat în funcția de rezervă), autorizează plătit a fi setat la adevărat indiferent dacă tranzacția cu eter a fost sau nu un succes. În acest caz, orice exploatator poate retrage fișierul câștigător câștigurile prin intermediul retireLeftOver Funcția.
Abordarea lui QuillAudit
Echipa noastră internă de dezvoltatori abordează această eroare cu ajutorul [transfer] funcție în loc de [trimite] funcție, deoarece [transfer] va reveni dacă tranzacția externă revine. Și dacă utilizați [trimite], verificați întotdeauna valoarea returnată.
Una dintre abordările robuste pe care le urmăm este utilizarea unui [model de retragere]. Aici, izolăm în mod logic funcționalitatea de trimitere externă de restul bazei de cod și plasăm tensiunea tranzacțiilor potențial eșuate pe utilizatorul final, deoarece acesta este cel care apelează funcția de retragere.
2. Reintrare
Contractele inteligente Ethereum apelează și utilizează coduri din alte contracte externe, iar pentru a efectua acest lucru, contractele trebuie să trimită apeluri externe. Aceste apeluri externe sunt vulnerabile și predispuse la atacuri, un astfel de atac a avut loc recent în cazul hack-ului DAO.
Vulnerabilitatea
Atacatorii efectuează astfel de atacuri atunci când un contract trimite eterul la o adresă necunoscută. În acest caz, atacatorul poate crea un contract la o adresă externă care posedă cod rău intenționat în funcția de rezervă, iar acest cod rău intenționat va fi invocat atunci când contractul trimite eter la această adresă.
Fapt: Termenul „reintroducere” a fost inventat din faptul că atunci când un contract extern rău intenționat apelează o funcție peste contractul vulnerabil și apoi calea de execuție a codului „o reintroduce”.
Luați în considerare exemplul de mai jos, este un seif Ethereum care permite deponenților să retragă doar 1 eter pe săptămână.
contractează EtherStore {
uint256 retragere publică Limită = 1 eter;
mapare (adresa => uint256) public lastWithdrawTime;
cartografiere (adresă => uint256) solduri publice;
function depositFunds () extern de plătit {
solduri [msg.sender] + = msg.value;
}
funcție retireFunds (uint256 _weiToWithdraw) public {
require (solduri [msg.sender]> = _weiToWithdraw);
// limitează retragerea
require (_weiToWithdraw <= extractionLimit);
// limitează timpul permis pentru retragere
require (acum> = lastWithdrawTime [msg.sender] + 1 săptămâni);
require (msg.sender.call.value (_weiToWithdraw) ());
solduri [msg.sender] - = _weiToWithdraw;
lastWithdrawTime [msg.sender] = acum;
}
}
În contractul de mai sus, avem două funcții publice, [depositFunds] și [retireFunds]. [DepositFunds] este utilizat pentru a crește soldul expeditorului, în timp ce [retireFunds] specifică suma de retras. În acest caz, va fi un succes dacă suma de retras este mai mică de 1 eter.
Capcana aici se află în linia [17] unde are loc transferul eterului. Atacatorul ar putea crea un contract rău intenționat cu adresa contractului [EtherStores] ca singurul parametru constructor. Acest lucru ar face [etherStore] o variabilă publică, deci mai predispusă la atac.
Abordarea lui QuilllAudit
Urmărim diverse tehnici pentru a evita potențialele vulnerabilități de reintrare în contractele inteligente. Primul și cel mai bun mod posibil este utilizarea funcției încorporate [transfer] la transferul eterului la orice contract extern.
În al doilea rând, este important să vă asigurați că toate modificările logice ale variabilelor de stare ar trebui să fie efectuate înainte de a trimite eterul din contract. În exemplul [EtherStore], liniile [18] și [19] ar trebui puse înainte de linia [17].
O a treia tehnică poate fi, de asemenea, utilizată pentru a preveni apelurile reentrante; prin introducerea unui mutex. Este o adăugare a unei variabile de stare care va bloca contractul în timpul executării codului.
3. Vizibilități implicite
Există specificatori de vizibilitate pentru funcțiile pe care le folosim în Solidity și prescriu modul în care pot fi numite. Vizibilitatea determină apelarea funcțiilor; extern de către utilizatori, prin alte contracte derivate, numai intern sau numai extern. Să ne uităm la modul în care utilizarea eronată a specificatorilor de vizibilitate poate provoca o vulnerabilitate imensă în contractele inteligente.
Vulnerabilitatea
În mod implicit, vizibilitatea funcției este [publică], prin urmare utilizatorii externi pot apela funcțiile fără vizibilitate specifică. Bug-ul apare atunci când dezvoltatorii uită să specifice vizibilitatea funcțiilor care ar trebui să fie private (sau care pot fi apelate în cadrul contractului). De exemplu;
contract HashForEther {
funcție retireCâștiguri () {
// Câștigător dacă ultimele 8 caractere hexagonale ale adresei sunt 0
require (uint32 (msg.sender) == 0);
_sendWinnings ();
}
function _sendWinnings () {
msg.sender.transfer (acest.echilibru);
}
}
Contractul de mai sus este un simplu joc de recompensă pentru a ghici adresa. În acest sens, putem vedea că vizibilitatea funcțiilor nu este specificată, în special funcția [_sendWinnings] este [publică] (implicit), prin urmare aceasta poate fi apelată prin orice adresă pentru a fura recompensa.
Abordarea lui QuillAudit
Echipa noastră internă este formată din dezvoltatori experimentați care respectă întotdeauna cele mai bune practici de audit, aici vizibilitatea funcțiilor ar trebui specificată în mod explicit, chiar dacă acestea trebuie menținute publice, ar trebui menționat.
4. Protejarea utilizării constructorilor
În general, constructorii sunt numiți funcții speciale care sunt folosite pentru a efectua sarcini critice și privilegiate în timpul inițializării contractelor. Înainte de Solidity [v0.4.22], constructorii dețineau același nume folosit de contractul care le conținea. Acum, luați în considerare un caz în care numele contractului este schimbat în timpul fazei de dezvoltare, dar numele constructorului rămâne același, această lacună poate oferi atacatorilor o intrare ușoară în contractul dvs. inteligent.
Vulnerabilitatea
Poate duce la consecințe grave dacă numele contractului este modificat, dar numele constructorului este neschimbat. De exemplu:
contract OwnerWallet {
adresa proprietarului public;
// constructor
funcție proprietar Portofel (adresă _proprietar) public {
proprietar = _proprietar;
}
// Da înapoi. Colectează eterul.
funcție () de plătit {}
function retire () public {
require (msg.sender == proprietar);
msg.sender.transfer (acest.echilibru);
}
}
În contractul de mai sus, putem vedea că numai proprietarul poate retrage eterul apelând funcția [retrage]. Aici, vulnerabilitatea apare pe măsură ce constructorul este numit diferit de contract (prima literă este diferită!). Astfel, exploatatorul poate apela funcția [ownerWallet] și se poate autoriza ca proprietar și apoi retrage tot eterul din contract apelând [retrage].
Abordarea lui QuillAudit
Ne conformăm cu versiunea [0.4.22] a compilatorului Solidity. Această versiune a introdus un cuvânt cheie; [constructor] care necesită numele funcției pentru a se potrivi cu numele contractului.
5. Autentificare Tx.Origin
Aici, [Tx.Origin] este variabila globală a Solidity, conține adresa contului care inițial a executat apelul sau tranzacția. Această variabilă nu poate fi utilizată pentru autentificare, deoarece acest lucru face ca contractul să fie vulnerabil la atacurile de phishing.
Vulnerabilitatea
Contractele care autorizează utilizatorii prin intermediul variabilei [tx.origin] sunt expuse atacurilor externe care îi determină pe utilizatori să efectueze acțiuni autentificate asupra contractului eronat. Luați în considerare exemplul de mai jos:
Contract Phishable {
adresa proprietarului public;
constructor (adresa proprietarului) {
proprietar = _proprietar;
}
function () extern de plătit {} // colectează eterul
funcție retireAll (adresă _recipient) public {
require (tx.origin == proprietar);
_recipient.transfer (acest.echilibru);
}
}
Aici, la rândul [11], contractul autorizează funcția [retireAll] cu ajutorul [tx.origin].
Abordarea lui QuillAudit
În general, evităm utilizarea [tx.origin] pentru autorizare în contracte inteligente. Deși, utilizarea [tx.origin] nu este strict interzisă, are anumite cazuri de utilizare specifice. Putem folosi [tx.origin] pentru a refuza contractele externe de la apelarea prezentului contract, acesta poate fi executat cu [require] a formei [require (tx.origin == msg.sender)]. Se face pentru a evita apelarea contractelor intermediare pentru a apela contractul actual, care limitează contractul la adrese regulate fără cod.
Încheierea finală
Am acoperit în mod cuprinzător cele cinci capcane comune în limba Solidity. În timp ce dezvoltăm contracte inteligente, nu trebuie să uităm că sunt imuabile prin design, ceea ce înseamnă că, odată ce le-am creat, nu există nicio modalitate de a corela codul sursă.
Acest lucru prezintă o mare provocare dezvoltatorilor de a profita de instrumentele de testare și audit de securitate disponibile înainte de implementare.
Descoperirea potențialelor amenințări rău intenționate pentru contractele inteligente și a riscurilor pe care le-am menționat mai sus sunt efectuate într-un mod foarte unic și robust de către echipa noastră internă de experți în audit. Noi de la QuillAudits depunem toate eforturile noastre în cercetarea securității pentru a vă menține contractul actualizat cu toate practicile de securitate software pentru a vă menține contractul în siguranță.
Contactați QuillHash
Cu o prezență industrială de ani de zile, QuillHash a livrat soluții de întreprindere în întreaga lume. QuillHash, cu o echipă de experți, este o companie lideră în dezvoltarea blockchain-ului care oferă diverse soluții din industrie, inclusiv întreprinderea DeFi. Dacă aveți nevoie de asistență în auditul contractelor inteligente, nu ezitați să contactați experții noștri aici!
Urmăriți QuillHash pentru mai multe actualizări
Sursa: https://blog.quillhash.com/2021/06/04/top-5-common-errors-in-solidity-programming-language/
- 11
- Cont
- Avantaj
- TOATE
- de audit
- Autentificare
- autorizare
- CEL MAI BUN
- blockchain
- Bug
- gandaci
- apel
- cazuri
- Provoca
- contesta
- cod
- Comun
- companie
- contract
- contracte
- Curent
- DAO
- DEFI
- Amenajări
- Dezvoltator
- Dezvoltatorii
- Dezvoltare
- Afacere
- Eter
- ethereum
- eveniment
- experți
- financiar
- First
- urma
- formă
- Gratuit
- funcţie
- viitor
- joc
- GAS
- Caritate
- mare
- hack
- aici
- Cum
- HTTPS
- mare
- Inclusiv
- industrie
- IT
- limbă
- conduce
- conducere
- Linie
- Listă
- Meci
- Altele
- proprietar
- Plasture
- Model
- Phishing
- atacuri de phishing
- prescrie
- prezenta
- privat
- Programare
- public
- trăgând
- cercetare
- răspuns
- REST
- sigur
- securitate
- Servicii
- set
- simplu
- mic
- inteligent
- contract inteligent
- Contracte inteligente
- So
- Software
- soliditate
- soluţii
- Stat
- succes
- Testarea
- Sursa
- amenințări
- timp
- top
- top 5
- tranzacție
- Tranzacții
- us
- utilizatorii
- valoare
- Boltă
- vizibilitate
- Vulnerabilitățile
- vulnerabilitate
- vulnerabil
- săptămână
- OMS
- în
- ani