Follow Us
Facebooktwitteryoutube
YouTube
Promo
banner
Promo
banner

Препоръки за защита на смарт договор Ethereum

блог 1NewsDevelopersEnterpriseBlockchain ExplainedEvent and ConferencesPressБюлетини

Contents

Абонирайте се за нашия бюлетин.

Имейл адрес

Ние уважаваме вашата поверителност

Развитие на HomeBlogBlockchain

Препоръки за защита на смарт договор Ethereum

От това как да се справим с външни обаждания към схеми за ангажименти, ето 10+ модела за защита на интелигентни договори, които да следвате, когато надграждате на Ethereum.by ConsenSys 10 юли 2020 г. Публикувано на 10 юли 2020 г.

Препоръки за защита на смарт договор Ethereum

Както разгледахме в Smart Contract Security Mindset, бдителният разработчик на Ethereum винаги поддържа пет принципа:

  • Подгответе се за провал
  • Разпространете внимателно
  • Опростете договорите
  • Бъдете в течение
  • Бъдете наясно с идиосинкразиите на EVM

В тази публикация ще се потопим в идиосинкразиите на EVM и ще разгледаме списък от модели, които трябва да следвате, когато разработвате каквато и да е система за интелигентни договори в Ethereum. Това парче е предимно за средни разработчици на Ethereum. Ако все още сте в ранните етапи на проучване, разгледайте програмата за разработчици на blockchain на ConsenSys Academy. 

Добре, нека се потопим.

Външни разговори

Внимавайте, когато правите външни повиквания

Обажданията към ненадеждни интелигентни договори могат да доведат до няколко неочаквани риска или грешки. Външните повиквания могат да изпълняват злонамерен код в този договор или друг договор, от който зависи. Затова третирайте всяко външно обаждане като потенциален риск за сигурността. Когато не е възможно или е нежелателно да се премахнат външни повиквания, използвайте препоръките в останалата част на този раздел, за да сведете до минимум опасността.

Маркирайте недоверени договори

Когато взаимодействате с външни договори, дайте име на вашите променливи, методи и интерфейси на договори по начин, който ясно показва, че взаимодействието с тях е потенциално опасно. Това се отнася за вашите собствени функции, които извикват външни договори.

// лоша банка.withdraw (100); // Неясно дали доверената или ненадеждната функция makeWithdrawal (uint сума) {// Не е ясно, че тази функция е потенциално опасна Bank.withdraw (сума); } // добър UntrustedBank.withdraw (100); // ненадеждно външно обаждане TrustedBank.withdraw (100); // външен, но доверен банков договор, поддържан от функцията XYZ Corp makeUntrustedWithdrawal (uint сума) {UntrustedBank.withdraw (сума); } Език на кода: PHP (php)

Избягвайте промени в състоянието след външни повиквания


Независимо дали използвате необработени повиквания (от формата someAddress.call ()) или договорни повиквания (от формата ExternalContract.someMethod ()), приемете, че може да се изпълни злонамерен код. Дори ако ExternalContract не е злонамерен, злонамерен код може да бъде изпълнен от всякакви договори, които извика.

Една особена опасност е злонамереният код да отвлече контролния поток, което да доведе до уязвимости поради повторно влизане. (Вижте Възобновяване за по-пълно обсъждане на този проблем).

Ако се обаждате към ненадежден външен договор, избягвайте промени в състоянието след обаждането. Този модел се нарича понякога и модел проверки-ефекти-взаимодействия.

Вижте SWC-107

Не използвайте transfer () или send ().

.transfer () and.send () препраща точно 2300 газ до получателя. Целта на тази твърдо кодирана газова стипендия беше да се предотврати уязвимости при повторно влизане, но това има смисъл само при предположението, че разходите за газ са постоянни. EIP 1884, който беше част от хардуера на Истанбул, увеличи цената на бензина при операцията SLOAD. Това накара резервната функция на договора да струва повече от 2300 газ. Препоръчваме да спрете да използвате .transfer () и.send () и вместо това да използвате.call ().

// лош договор Уязвим {теглене на функция (uint256 количество) външен {// Това препраща 2300 газ, което може да не е достатъчно, ако получателят // е договор и разходите за газ се променят. msg.sender.transfer (сума); }} // добър договор Фиксиран {изтегляне на функция (uint256 сума) външен {// Това препраща целия наличен газ. Не забравяйте да проверите връщаната стойност! (bool успех,) = msg.sender.call.value (сума) (""); изискват (успех, "Прехвърлянето не бе успешно."); }} Език на кода: JavaScript (javascript)

Имайте предвид, че .call () не прави нищо за смекчаване на атаките за повторно влизане, така че трябва да се вземат други предпазни мерки. За да предотвратите повторни атаки, използвайте модел проверки-ефекти-взаимодействия.

Обработвайте грешки при външни повиквания

Solidity предлага методи за повикване на ниско ниво, които работят върху сурови адреси: address.call (), address.callcode (), address.delegatecall () и address.send (). Тези методи на ниско ниво никога не извеждат изключение, но ще върнат false, ако повикването срещне изключение. От друга страна, повикванията по договор (напр. ExternalContract.doSomething ()) автоматично ще разпространяват хвърляне (например ExternalContract.doSomething () също ще хвърля, ако doSomething () хвърля).

Ако решите да използвате методите за повикване на ниско ниво, уверете се, че се справяте с възможността повикването да се провали, като проверите връщаната стойност.

// лош someAddress.send (55); someAddress.call.value (55) (""); // това е двойно опасно, тъй като ще препрати целия останал газ и не проверява за резултат someAddress.call.value (100) (bytes4 (sha3 ("депозит ()"))); // ако депозитът хвърли изключение, суровото повикване () ще върне само false и транзакцията НЯМА да бъде върната // добра (успех на bool,) = someAddress.call.value (55) (""); if (! успех) {// обработка на код за грешка} ExternalContract (someAddress) .deposit.value (100) (); Език на кода: JavaScript (javascript)

Вижте SWC-104

Изтегляне на предпочитание за външни разговори

Външните повиквания могат да се провалят случайно или умишлено. За да сведете до минимум щетите, причинени от такива неуспехи, често е по-добре да изолирате всяко външно повикване в собствена транзакция, която може да бъде инициирана от получателя на повикването. Това е особено важно за плащания, при които е по-добре да позволите на потребителите да теглят средства, вместо да им изпращат средства автоматично. (Това също намалява шанса за проблеми с ограничението на газта.) Избягвайте да комбинирате множество етерни трансфери в една транзакция.

// търг с лоши договори {адрес най-високата оферта; uint най-висока оферта; функция оферта () платимо {изисква (msg.value >= най-висока оферта); ако (най-висока оферта! = адрес (0)) {(успех на bool,) = най-висока стойност.call.value (най-висока оферта) (""); изискват (успех); // ако това обаждане постоянно се проваля, никой друг не може да наддава} най-високата оферта = msg.sender; най-висока оферта = msg.value; }} // търг за добър договор {адрес най-високата оферта; uint най-висока оферта; картографиране (адрес => uint) възстановяване на суми; функция оферта () платима външна {изисква (msg.value >= най-висока оферта); ако (най-висока оферта! = адрес (0)) {възстановявания [най-висока оферта] + = най-висока оферта; // записваме възстановяването на сумата, което този потребител може да поиска} najvišjiBidder = msg.sender; най-висока оферта = msg.value; } функция изтеглянеRefund () външен {възстановяване на връщане = връщане на суми [msg.sender]; възстановяване [msg.sender] = 0; (bool успех,) = msg.sender.call.value (възстановяване) (""); изискват (успех); }} Език на кода: JavaScript (javascript)

Вижте SWC-128

Не делегирайте обажданията към ненадежден код

Функцията delegatecall извиква функции от други договори, сякаш принадлежат към договора за повикващия. По този начин повиканият може да промени състоянието на адреса за повикване. Това може да е несигурно. Пример по-долу показва как използването на delegatecall може да доведе до унищожаване на договора и загуба на баланса му.

договор Destructor {функция doWork () external {selfdestruct (0); }} договор за работник {функция doWork (адрес _internalWorker) public {// unsafe _internalWorker.delegatecall (bytes4 (keccak256 ("върши работа()"))); }} Език на кода: JavaScript (javascript)

Ако Worker.doWork () бъде извикан с адреса на разположения договор за деструктор като аргумент, договорът на Worker ще се самоунищожи. Делегирайте изпълнението само на доверени договори и никога до предоставен от потребителя адрес.

Внимание

Не приемайте, че договорите са създадени с нулев баланс. Нападателят може да изпрати етер на адреса на договор, преди той да бъде създаден. Договорите не трябва да приемат, че първоначалното му състояние съдържа нулев баланс. Вижте брой 61 за повече информация.

Вижте SWC-112

Не забравяйте, че етерът може да бъде принудително изпратен към акаунт

Пазете се от кодиране на инвариант, който стриктно проверява баланса на даден договор.

Нападателят може принудително да изпрати етер до всеки акаунт. Това не може да бъде предотвратено (дори с резервна функция, която прави revert ()).

Нападателят може да направи това, като създаде договор, финансира го с 1 wei и се позове на самоунищожение (жертваАдрес). Не се извиква код в AddressAddress, така че не може да бъде предотвратен. Това важи и за блоковото възнаграждение, което се изпраща на адреса на майнера, който може да бъде произволен адрес.

Също така, тъй като адресите на договора могат да бъдат предварително изчислени, етерът може да бъде изпратен на адрес, преди да бъде разположен договорът.

Вижте SWC-132

Не забравяйте, че данните по веригата са публични

Много приложения изискват подадените данни да са частни до някакъв момент от време, за да работят. Игри (напр. Верижни скални хартиени ножици) и тръжни механизми (напр. Запечатана оферта Търгове на Викри) са две основни категории примери. Ако създавате приложение, при което поверителността е проблем, уверете се, че избягвате да изисквате от потребителите да публикуват информация твърде рано. Най-добрата стратегия е да се използва схеми за ангажименти с отделни фази: първо фиксирайте с помощта на хеш на стойностите и в по-късна фаза разкриване на стойностите.

Примери:

  • При ножици за хартия от хартия, изисквайте и двамата играчи да представят първо хеш на планирания ход, след което изисквайте и двамата играчи да представят хода си; ако подаденият ход не съответства на хеш, изхвърлете го.
  • При търг изисквайте от играчите да представят хеш на стойността на офертата си в начална фаза (заедно с депозит, по-голям от стойността на офертата им), и след това да подадат стойността на офертата си на търг във втората фаза.
  • При разработването на приложение, което зависи от генератор на случайни числа, поръчката винаги трябва да бъде (1) играчите подават ходове, (2) генерирани произволни числа, (3) играчите получават изплащане. Много хора активно изследват генераторите на случайни числа; текущите най-добри в класа решения включват заглавки на блокове Bitcoin (проверени чрез http://btcrelay.org), схеми за хеш-ангажиране-разкриване (т.е. едната страна генерира номер, публикува своя хеш, за да се „ангажира“ със стойността и след това разкрива стойността по-късно) и РАНДАО. Тъй като Ethereum е детерминиран протокол, не можете да използвате никоя променлива в протокола като непредсказуемо произволно число. Също така имайте предвид, че майньорите в известна степен контролират стойността block.blockhash ()*.

Пазете се от възможността някои участници да „отпаднат офлайн“ и да не се върнат

Не правете процесите на възстановяване на суми или искове зависими от конкретна страна, изпълняваща определено действие, без друг начин за изваждане на средствата. Например, в играта на ножица с хартия-хартия, една често срещана грешка е да не се прави изплащане, докато и двамата играчи не подадат ходовете си; зловредният играч обаче може да „наскърби“ другия, като просто никога не подава хода си – всъщност, ако играч види разкрития ход на другия играч и установи, че е загубил, той изобщо няма причина да подава собствен ход. Този проблем може да възникне и в контекста на уреждане на държавни канали. Когато такива ситуации са проблем, (1) осигурете начин за заобикаляне на неучастващите участници, може би чрез определен срок, и (2) помислете за добавяне на допълнителен икономически стимул за участниците да подават информация във всички ситуации, в които се намират трябва да го направи.

Пазете се от отрицание на най-отрицателното цяло число

Solidity предоставя няколко типа за работа с подписани цели числа. Както в повечето езици за програмиране, в Solidity подписано цяло число с N бита може да представлява стойности от -2 ^ (N-1) до 2 ^ (N-1) -1. Това означава, че няма положителен еквивалент за MIN_INT. Отрицанието се изпълнява като намиране на допълнението на две на число, така че отрицанието на най-отрицателното число ще доведе до същия брой. Това важи за всички подписани цели числа в Solidity (int8, int16,…, int256).

договор Отрицание {функция negate8 (int8 _i) публично чисти връщания (int8) {return -_i; } функция negate16 (int16 _i) публично чисти връщания (int16) {return -_i; } int8 public a = negate8 (-128); // -128 int16 public b = negate16 (-128); // 128 int16 public c = negate16 (-32768); // -32768} Език на кода: PHP (php)

Един от начините да се справите с това е да проверите стойността на променлива преди отрицанието и да хвърлите дали тя е равна на MIN_INT. Друг вариант е да се уверите, че най-отрицателното число никога няма да бъде постигнато, като използвате тип с по-голям капацитет (например int32 вместо int16).

Подобен проблем с типовете int възниква, когато MIN_INT се умножава или дели на -1.

Сигурен ли е вашият блокчейн код? 

Надяваме се, че тези препоръки са били полезни. Ако вие и вашият екип се подготвяте за стартиране или дори в началото на жизнения цикъл на разработката и се нуждаете от проверка на здравословното състояние на вашите интелигентни договори, моля не се колебайте да се свържете с нашия екип от инженери по сигурността в ConsenSys Diligence. Ние сме тук, за да ви помогнем да стартирате и поддържате вашите Ethereum приложения със 100% увереност. 

Резервирайте проверка на място за сигурност

Резервирайте еднодневен преглед с нашия екип от експерти по сигурността на блокчейн. Резервирайте днес вашите SecuritySmart ContractsNewsletter Абонирайте се за нашия бюлетин за най-новите новини на Ethereum, корпоративни решения, ресурси за разработчици и други.Как да изградим успешен блокчейн продуктУебинар

Как да изградим успешен блокчейн продукт

Как да настроите и стартирате Ethereum NodeУебинар

Как да настроите и стартирате Ethereum Node

Как да създадете свой собствен API за EthereumУебинар

Как да създадете свой собствен API за Ethereum

Как да създадете социален токенУебинар

Как да създадете социален токен

Използване на инструменти за сигурност при разработването на интелигентен договорУебинар

Използване на инструменти за сигурност при разработването на интелигентен договор

Бъдещето на финансите Digital Assets и DeFiУебинар

Бъдещето на финансите: цифрови активи и DeFi

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