Берегите себя и свои эфиры
Безопасность смарт контрактов обеспечивается правильным доступам к функционалу. Ошибки в контрактах могут привести к огромным потерям в будущем. Это проблема не только мелких проектов. Даже крупные и популярные сервисы страдали от элементарных дыр в коде (Compound,, Value.defi). Сегодня рассмотрим способы обезопасить свой контракт, разберем самые именитые ошибки и как бонус - полезная утилита. Делай себе кофий, юзернейм, и читай дальше.
1. Reentrancy
Из-за этой ошибки в свое время пострадал Compound. Из-за нее ее же появился форк, который ты знаешь как Ethereum Classic. Уязвимый контракт вызывает другой контракт, а внешний контракт совершает ответный вызов внутри начального вызова. А теперь для людей: В твоем контракте вызывается метод withdraw, который передаст вызвавшему количество эфира, которое он заложил на вашем контракте. Казалось бы, что может пойти не так? Тут на вызывающем контракте вызывается метод withdraw, а в fallback функции снова вызывается метод withdraw. Далее хакер вызвает withdraw на 1 эфир. Валюта переходит на контракт хакера, что триггерит вновь функцию withdraw. Таким образом хакер выкачивает все деньги до нуля, даже если в требованиях ему положенно брать только свои деньги. В библиотеке OpenZeppelin уже есть готовая реализация ReentrancyGuard. Если ваш контракт вызыает в методе неопределенный контракт, то убедитесь что у вас есть модификатор, который предотвращает рекурсивный вызов.
2. Access Control
Даже если в твоем контракте есть модификатор только для владельца - есть способ перехватить доступ к твоему методу. К примеру у тебя есть функция withdrawAll, которая позволяет снять все эфиры со счета, доступная только владельцу. Контракт хакра создает инстанс твоего контракта внутри себя, а в fallback функции вызывает метод withdrawAll. Теперь осталось только заставить тебя перевести на контракт хакера хотя бы малость эфира. В fallback срабатывает withdrawAll и снимает все эфиры со счета твоего контракта, ведь, не смотря на модификатор, ты сам вызвал этот метод. Действенных способ - использовать только msg.sender в модификаторе доступа. Он определяет последний адрес как адрес вызывающего и в примере выше msg.sender будет не твой адрес, а адрес контракта хакера. Следовательно, у него ничего не получится. Избегайте использования tx.origin. Контракт Ownable в OpenZeppelin уже предоставляет надежные модификаторы доступа.
3. Common math
Проблема оверфлоу так же достаточно часто встречается в контрактах. В версии Solidity 0.8.4 эта ошибка была исправлена, но что если прийдется писать контракт на версиях ниже? Максимальное значение типа uint - 2^256-1, а минимальное -0. Если мы отнимем от нуля единицу то мы получим... 2^256-1! Так же если к 2^256-1 прибавить единицу, то мы получим... верно - ноль. Обьяснять как это могут применить хакеры, думаю, не стоит. Но, достаточно пары простых проверок после арифметической операции, что б узнать не случился ли оверфлоу, а вообще, у OpenZeppelin уже есть готовая библиотека SafeMath для таких случаев.
4. Unchecked send
К примеру, твоем контракте-лотерее ты должен отправить победителю эфиры. Срабатывает функция send и на счет юзернейму приходит приз. Да толко срабатывает send не всегда. ЕВМ имеет ограниченный ресурс вызовов - callstack. Если callstack заполнен, то send не сработает. От этого спасет элементарная проверка сразу после вызова send.
Это самые частые, но далеко не все возможные уязвимости смарт контрактов. Чаще всего они возникают из-за невнимательности во время написания и небрежного тестирования. При тестировании обязательно учитывай все возможные варианты выполнения контракта, а с этим тебе, юзернейм, поможет slither-analyzer. Утилита на Python анализирует твой контракт и выявляет все явные уязвимости. Но не надейся что приблуда сделает все за тебя. Она лишь упрощает процесс поиска уязвимостей, но все остается в твоих руках. Безопасность - выше всего.
4 comments