Рекомендации по безопасности смарт-контрактов Ethereum

блог 1НовостиДля разработчиковПредприятиеБлокчейнПояснениеМероприятия и конференцииПрессаИнформационные бюллетени

Contents

Подписывайтесь на нашу новостную рассылку.

Адрес электронной почты

Мы уважаем вашу конфиденциальность

ГлавнаяБлогРазработка блокчейна

Рекомендации по безопасности смарт-контрактов Ethereum

От того, как обрабатывать внешние вызовы до схем обязательств, вот 10+ шаблонов безопасности смарт-контрактов, которым нужно следовать при создании Ethereum. By ConsenSys10 июля 2020 г.Опубликовано 10 июля 2020 г.

Рекомендации по безопасности смарт-контрактов Ethereum

Как мы уже говорили в статье о безопасности смарт-контрактов, бдительный разработчик Ethereum всегда руководствуется пятью принципами:

  • Готовьтесь к неудачам
  • Развертывайте осторожно
  • Держите контракты простыми
  • Будьте в курсе
  • Помните об особенностях EVM

В этом посте мы углубимся в особенности EVM и рассмотрим список шаблонов, которым вы должны следовать при разработке любой системы смарт-контрактов на Ethereum. Эта статья предназначена в первую очередь для разработчиков Ethereum среднего уровня. Если вы все еще на начальной стадии исследования, ознакомьтесь с программой разработки блокчейнов ConsenSys Academy по требованию.. 

Хорошо, давай нырнем.

Внешние звонки

Соблюдайте осторожность при совершении внешних звонков

Обращение к ненадежным смарт-контрактам может привести к нескольким неожиданным рискам или ошибкам. Внешние вызовы могут выполнять вредоносный код в этом контракте или любом другом контракте, от которого он зависит. Поэтому относитесь к каждому внешнему вызову как к потенциальной угрозе безопасности. Когда невозможно или нежелательно удалить внешние вызовы, используйте рекомендации в остальной части этого раздела, чтобы минимизировать опасность..

Отметить ненадежные контракты

При взаимодействии с внешними контрактами назовите свои переменные, методы и интерфейсы контрактов таким образом, чтобы было ясно, что взаимодействие с ними потенциально небезопасно. Это относится к вашим собственным функциям, которые вызывают внешние контракты..

// плохой Bank.withdraw (100); // Неясно, является ли доверенная или ненадежная функция makeWithdrawal (uint amount) {// Не ясно, является ли эта функция потенциально опасной Bank.withdraw (amount); } // хороший UntrustedBank.withdraw (100); // ненадежный внешний вызов TrustedBank.withdraw (100); // внешний, но надежный банковский контракт, поддерживаемый XYZ Corp function makeUntrustedWithdrawal (uint amount) {UntrustedBank.withdraw (amount); } Язык кода: PHP (php)

Избегайте изменений состояния после внешних вызовов

При использовании необработанных вызовов (формы someAddress.call ()) или вызовов контракта (формы ExternalContract.someMethod ()) предполагается, что вредоносный код может выполняться. Даже если ExternalContract не является вредоносным, вредоносный код может выполняться любыми вызываемыми им контрактами..

Одна из особых опасностей заключается в том, что вредоносный код может захватить поток управления, что приведет к уязвимости из-за повторного входа. (Видеть Реентерабельность для более полного обсуждения этой проблемы).

Если вы вызываете ненадежный внешний контракт, избегайте изменений состояния после вызова. Этот паттерн также иногда называют шаблон проверок-эффектов-взаимодействий.

Видеть SWC-107

Не используйте transfer () или send ().

.transfer () и.send () пересылает получателю ровно 2300 единиц газа. Целью этой жестко запрограммированной стипендии на газ было предотвратить уязвимости повторного входа, но это имеет смысл только при условии, что затраты на газ постоянны.. EIP 1884, который был частью стамбульского хард-форка, увеличил стоимость газа для операции SLOAD. Это привело к тому, что резервная функция контракта стоила более 2300 единиц газа. Мы рекомендуем прекратить использование.transfer () и.send () и вместо этого использовать.call ().

// плохой контракт Vulnerable {function remove (uint256 amount) external {// Это пересылает 2300 единиц газа, чего может быть недостаточно, если // получатель является контрактом и стоимость газа изменится. msg.sender.transfer (сумма); }} // хороший контракт Фиксированный {function remove (uint256 amount) external {// Пересылает весь доступный газ. Обязательно проверьте возвращаемое значение! (bool success,) = 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 success,) = someAddress.call.value (55) (""); if (! success) {// обрабатываем код ошибки} ExternalContract (someAddress) .deposit.value (100) (); Язык кода: JavaScript (javascript)

Видеть SWC-104

Поддерживайте выталкивание для внешних вызовов

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

// аукцион плохих контрактов {address highBidder; uint highBid; функция bid () payable {require (msg.value >= высшая ставка); if (highBidder! = адрес (0)) {(bool success,) = highBidder.call.value (highBid) (""); требовать (успех); // если этот вызов постоянно терпит неудачу, никто другой не может делать ставки} highBidder = msg.sender; highBid = msg.value; }} // аукцион хороших контрактов {address highBidder; uint highBid; отображение (адрес => uint) возврат средств; функция bid () оплачиваемая внешняя {require (msg.value >= высшая ставка); if (highBidder! = адрес (0)) {возврат [highBidder] + = highBid; // записываем возврат, который может потребовать этот пользователь} highBidder = msg.sender; highBid = msg.value; } функция removewRefund () external {uint return = refunds [msg.sender]; возврат [msg.sender] = 0; (bool success,) = msg.sender.call.value (возврат) (""); требовать (успех); }} Язык кода: JavaScript (javascript)

Видеть SWC-128

Не делегируйте вызов ненадежному коду

Функция delegatecall вызывает функции из других контрактов, как если бы они принадлежали контракту вызывающего абонента. Таким образом, вызываемый абонент может изменить состояние вызывающего адреса. Это может быть небезопасно. Пример ниже показывает, как использование delegatecall может привести к разрушению контракта и потере его баланса..

контракт Деструктор {функция doWork () external {самоуничтожение (0); }} контракт Worker {function doWork (address _internalWorker) public {// unsafe _internalWorker.delegatecall (bytes4 (keccak256 ("Выполнять работу()"))); }} Язык кода: JavaScript (javascript)

Если Worker.doWork () вызывается с адресом развернутого контракта Destructor в качестве аргумента, контракт Worker самоуничтожается. Делегировать исполнение только доверенным контрактам, и никогда на адрес, указанный пользователем.

Предупреждение

Не думайте, что контракты заключаются с нулевым балансом. Злоумышленник может отправить эфир на адрес контракта до его создания. Контракты не должны предполагать, что их начальное состояние содержит нулевой баланс. Видеть Выпуск 61 Больше подробностей.

Видеть SWC-112

Помните, что эфир может быть принудительно отправлен на аккаунт

Остерегайтесь кодирования инварианта, который строго проверяет баланс контракта..

Злоумышленник может принудительно отправить эфир на любой аккаунт. Этого нельзя предотвратить (даже с помощью резервной функции, которая выполняет revert ()).

Злоумышленник может сделать это, создав контракт, оплатив его 1 вэй и вызвав самоуничтожение (адрес жертвы). Код жертвы не вызывается, поэтому его нельзя предотвратить. Это также верно для вознаграждения за блок, которое отправляется на адрес майнера, который может быть любым произвольным адресом..

Кроме того, поскольку адреса контрактов могут быть предварительно вычислены, эфир может быть отправлен на адрес до того, как контракт будет развернут..

Видеть SWC-132

Помните, что данные в сети являются общедоступными

Многие приложения требуют, чтобы отправленные данные были конфиденциальными до определенного момента времени для работы. Игры (например, ончейн “камень-ножницы-бумага”) и механизмы аукциона (например, запечатанная ставка Викри аукционы) – это две основные категории примеров. Если вы создаете приложение, где конфиденциальность является проблемой, убедитесь, что вы не требуете от пользователей публиковать информацию слишком рано. Лучшая стратегия – использовать схемы обязательств с отдельными фазами: сначала фиксация с использованием хеша значений, а на более поздней фазе раскрытие значений.

Примеры:

  • В игре «камень-ножницы-бумага» требуется, чтобы оба игрока сначала представили хэш своего предполагаемого хода, а затем потребовать от обоих игроков представить свой ход; если отправленный ход не соответствует хешу, выбросить его.
  • На аукционе потребуйте от игроков предоставить хеш-значение своей ставки на начальном этапе (вместе с депозитом, превышающим их значение ставки), а затем представить свою цену на аукционе на втором этапе..
  • При разработке приложения, которое зависит от генератора случайных чисел, порядок всегда должен быть следующим: (1) игроки отправляют ходы, (2) генерируются случайные числа, (3) игрокам выплачиваются выплаты. Многие люди активно исследуют генераторы случайных чисел; текущие лучшие в своем классе решения включают заголовки блоков биткойнов (проверено через 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 ContractsНовостная рассылкаПодпишитесь на нашу новостную рассылку, чтобы получать последние новости Ethereum, корпоративные решения, ресурсы для разработчиков и многое другое.Адрес электронной почтыЭксклюзивный контентКак создать успешный блокчейн-продуктВебинар

Как создать успешный блокчейн-продукт

Как настроить и запустить узел EthereumВебинар

Как настроить и запустить узел Ethereum

Как создать собственный API EthereumВебинар

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

Как создать социальный токенВебинар

Как создать социальный токен

Использование инструментов безопасности при разработке смарт-контрактовВебинар

Использование инструментов безопасности при разработке смарт-контрактов

Будущее финансовых цифровых активов и DeFiВебинар

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

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