Руководство по безопасности смарт-контрактов
Следуйте этим высокоуровневым рекомендациям для создания более безопасных смарт-контрактов.
Рекомендации по проектированию
Проектирование контракта следует обсудить заранее, до написания хотя бы одной строчки кода.
Документация и спецификации
Документация может быть написана на разных уровнях и должна обновляться в процессе реализации контрактов:
- Описание системы простым языком, объясняющее, что делают контракты, и любые допущения относительно кодовой базы.
- Схемы и архитектурные диаграммы, включая взаимодействия контрактов и машину состояний системы. Принтеры Слизер (opens in a new tab) могут помочь в генерации этих схем.
- Тщательная документация кода, для Solidity можно использовать формат NatSpec (opens in a new tab).
Вычисления ончейн и офчейн
- Держите как можно больше кода офчейн. Оставляйте ончейн-слой небольшим. Предварительно обрабатывайте данные с помощью кода офчейн таким образом, чтобы проверка ончейн была простой. Вам нужен упорядоченный список? Отсортируйте список офчейн, а затем только проверьте его порядок ончейн.
Возможность обновления
Мы обсуждали различные решения для обновления в нашем блоге (opens in a new tab). Сделайте осознанный выбор в пользу поддержки возможности обновления или отказа от нее до написания какого-либо кода. Это решение повлияет на то, как вы структурируете свой код. В целом, мы рекомендуем:
- Отдавать предпочтение миграции контрактов (opens in a new tab), а не возможности обновления. Системы миграции имеют много тех же преимуществ, что и обновляемые, но без их недостатков.
- Использовать паттерн разделения данных вместо паттерна delegatecallproxy. Если в вашем проекте есть четкое разделение абстракций, возможность обновления с использованием разделения данных потребует лишь нескольких корректировок. Использование delegatecallproxy требует глубоких знаний EVM и сопряжено с высоким риском ошибок.
- Документировать процедуру миграции/обновления до развертывания. Если вам придется реагировать в стрессовой ситуации без каких-либо инструкций, вы наделаете ошибок. Заранее напишите процедуру, которой следует придерживаться. Она должна включать:
- Вызовы, которые инициируют новые контракты
- Где хранятся ключи и как получить к ним доступ
- Как проверить развертывание! Разработайте и протестируйте скрипт, выполняемый после развертывания.
Рекомендации по реализации
Стремитесь к простоте. Всегда используйте самое простое решение, которое соответствует вашей цели. Любой член вашей команды должен быть в состоянии понять ваше решение.
Композиция функций
Архитектура вашей кодовой базы должна облегчать проверку кода. Избегайте архитектурных решений, которые снижают возможность рассуждать о его правильности.
- Разделяйте логику вашей системы, либо с помощью нескольких контрактов, либо путем группировки похожих функций (например, аутентификация, арифметика и т. д.).
- Пишите небольшие функции с четкой целью. Это облегчит проверку и позволит тестировать отдельные компоненты.
Наследование
- Сохраняйте наследование управляемым. Наследование следует использовать для разделения логики, однако ваш проект должен стремиться к минимизации глубины и ширины дерева наследования.
- Используйте принтер наследования (opens in a new tab) Слизер для проверки иерархии контрактов. Принтер наследования поможет вам оценить размер иерархии.
События
- Ведите лог всех важных операций. События помогут отлаживать контракт во время разработки и отслеживать его после развертывания.
Избегайте известных подводных камней
- Знайте о наиболее распространенных проблемах безопасности. Существует множество онлайн-ресурсов для изучения распространенных проблем, таких как Ethernaut CTF (opens in a new tab), Capture the Ether (opens in a new tab) или Not so smart contracts (opens in a new tab).
- Обращайте внимание на разделы с предупреждениями в документации Solidity (opens in a new tab). Разделы с предупреждениями проинформируют вас о неочевидном поведении языка.
Зависимости
- Используйте хорошо протестированные библиотеки. Импорт кода из хорошо протестированных библиотек снизит вероятность написания кода с ошибками. Если вы хотите написать контракт ERC-20, используйте ОпенЗеппелин (opens in a new tab).
- Используйте менеджер зависимостей; избегайте копирования и вставки кода. Если вы полагаетесь на внешний источник, вы должны поддерживать его в актуальном состоянии в соответствии с оригиналом.
Тестирование и верификация
- Пишите тщательные модульные тесты. Обширный набор тестов имеет решающее значение для создания высококачественного программного обеспечения.
- Пишите пользовательские проверки и свойства для Слизер (opens in a new tab), Эхидна (opens in a new tab) и Мантикора (opens in a new tab). Автоматизированные инструменты помогут убедиться в безопасности вашего контракта. Ознакомьтесь с остальной частью этого руководства, чтобы узнать, как писать эффективные проверки и свойства.
- Используйте crytic.io (opens in a new tab). Crytic интегрируется с GitHub, предоставляет доступ к приватным детекторам Слизер и запускает пользовательские проверки свойств от Эхидна.
Solidity
- Отдавайте предпочтение Solidity 0.5 вместо 0.4 и 0.6. По нашему мнению, Solidity 0.5 более безопасен и имеет лучшие встроенные практики, чем 0.4. Версия Solidity 0.6 оказалась слишком нестабильной для продакшена и требует времени для доработки.
- Используйте стабильный релиз для компиляции; используйте последний релиз для проверки предупреждений. Убедитесь, что в вашем коде нет проблем, о которых сообщает последняя версия компилятора. Однако Solidity имеет быстрый цикл выпуска и историю ошибок компилятора, поэтому мы не рекомендуем использовать последнюю версию для развертывания (см. рекомендации по версии solc (opens in a new tab) от Слизер).
- Не используйте встроенный ассемблер. Ассемблер требует глубоких знаний EVM. Не пишите код для EVM, если вы не освоили желтую книгу.
Рекомендации по развертыванию
После того как контракт был разработан и развернут:
- Мониторьте свои контракты. Следите за логами и будьте готовы отреагировать в случае компрометации контракта или кошелька.
- Добавьте свою контактную информацию в blockchain-security-contacts (opens in a new tab). Этот список помогает третьим лицам связаться с вами в случае обнаружения уязвимости в системе безопасности.
- Обеспечьте безопасность кошельков привилегированных пользователей. Следуйте нашим лучшим практикам (opens in a new tab), если вы храните ключи в аппаратных кошельках.
- Имейте план реагирования на инциденты. Учитывайте, что ваши смарт-контракты могут быть скомпрометированы. Даже если в ваших контрактах нет ошибок, злоумышленник может завладеть ключами владельца контракта.