Безопасность смарт-контрактов

Краткая версия доклада

Начиная разговор об уязвимостях смарт-контрактов, типичных ошибках, которые совершают их разработчики, а также о причинах возникновения этих ошибок, необходимо понимать, что код, написанный разработчиком, будет использоваться в рамках сети, участники которой договорились поддерживать определенную структуру данных между собой, т.е. в блокчейне. Особенностью использования блокчейна является отсутствие гарантий исполнения порядка транзакций, что повышает их уязвимость для front-running attack - манипуляций за счет обладания закрытой информацией о транзакциях в состоянии ожидания. Атакующий имеет возможность отследить рассылаемую транзакцию, помещенную в пул неподтвержденных транзакций, и отправить свою транзакцию с тем же числом, но чуть большей комиссией. С высокой вероятностью его блок будет обработан смарт-контрактом первым. Кроме того, в транзакциях нельзя полагаться на метки времени. Когда майнер сформировал блок, он помещает туда time-stamp, указывающий, когда он это сделал, при этом метка времени должна быть больше, чем в предыдущем блоке. При валидации блока time-stamp не должен быть помещен в будущее. Принимая во внимание, что на нахождение нового блока у сети уходит от 12 до 17 секунд, получается, что у майнера есть несколько секунд на манипуляцию меткой времени, поэтому ее нельзя использовать как сид для генератора псевдослучайных чисел (PRNG) или для принятия решений в конкурирующем процессе.

В этих условиях полностью исключено использования генераторов случайных чисел для составления смарт-контрактов, все переменные окружения могут быть просчитаны, и такую систему можно успешно атаковать. Открытые данные в контракте могут с легкостью стать добычей майнеров. Для решения этой проблемы хэш блока можно использовать как сид PRNG с двумя ограничениями: установив время хэша блока на какой-либо момент в будущем, а также учитывая, что у пользователя есть только последние 256 блоков для проведения транзакции, после чего функция начнет возвращать 0. Для того, чтобы исключить участие злоумышленников, можно использовать схему commit-reveal (участники отправляют смарт-контракту хеш от случайного числа с некоторым депозитом, а затем посылают свои загаданные числа смарт-контракту, а тот проверяет число, взяв от него хеш; после того, как все отправили числа, контракт использует их как сид для PRNG). Недостаток схемы очевиден - она подвержена DOS. Как вариант - можно объединить идею с хешем блока и схемой commit-reveal. Еще один вариант, заслуживающий внимания, - схема Signidice, реализующая логику игры в казино, которое генерирует пару приватный-открытый ключ и отправляет публичный смарт-контракт стороннему игроку, который делает свою ставку, высылая случайное число. После серии обменов подписью та используется в качестве сида для PRNG.

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

При этом нужно учитывать, что используемый в смарт-контракте код используется специально разработанной виртуальной машиной - Ethereum Virtual Machine, EVM). В числе ее уязвимостей - переполнение целочисленных типов, не знающее исключений; подверженность short-address attack, за счет особенностей адресации в EVM меняющей логику работы контракта; type confusion, вызванный тем, что виртуальная машина не ведет проверки типов; смена владельца контракта по схеме uninitialized storage pointer. К логическим уязвимостям смарт-контракта можно отнести ошибки в написании имен переменных, отсутствие модификаторов доступа для функции, ошибки предметной области.

Среди инструментов для решения этих проблем можно назвать IDE (интегрированную среду разработки), которая "подсвечивает" множество потенциальных ошибок при написании смарт-контракта, одна из лучших - remix.etherium.org. Помимо этого, укажем на систему онлайн-верификации смарт-контрактов securify.ch, линтеры Solint и Solcheck, фреймворки Truffle с возможностью тестирования кода, библиотеки на web3, с помощью которых можно сделать удаленный вызов процедур для проверки контракта. Интересным вариантом обеспечения безопасности для смарт-контракта является создание вокруг него группы аналогичных контрактов, тестирующих основной. Весьма эффективна схема символьного исполнения, так как многие возможные проблемы уже заложены в дизайн виртуальной машины (системы Oyente, Manticore, Dry-analyzer, дебаги remix.etherium.org и Radare2, Mythril и Porosity).

Для тех, кто решит реализовать проект со смарт-контрактом, мы рекомендуем ориентироваться на лучшие практики, используя уже обкатанные решения. Кроме того, вне зависимости от тактики действий, всегда нужно проводить аудит кода, а также пользоваться сервисом Bug Bounty.