Немного обо мне
Я пришел в блокчейн в 2015 году, как и многие другие, энтузиастом. В то время технология была малоизвестной, но меня заинтересовала возможность майнить биткоин и зарабатывать — ко всему прочему, тогда у меня было множество серверов, так что это было несложно. Вскоре я решил заняться коммерческой разработкой и стал главой команды Zero Cool Team. Активное участие в питерской ассоциации разработчиков блокчейна также стало частью моей жизни.
В 2018 году я присоединился к стартапу Akropolis, где стал техническим директором и ведущим инженером. В ходе работы наш проект взломали, и тогда я впервые серьезно задумался о безопасности. После ухода из Akropolis я занялся индивидуальной разработкой, а также начал проводить аудиты смарт-контрактов.
Этот опыт привел меня в компанию Oxorio, где я сегодня работаю в качестве тимлида и ведущего аудитора.
Что такое аудит смарт-контрактов
Смарт-контракты — это специальные цифровые соглашения, которые выполняются сами по себе и без которых сегодня сложно представить технологию блокчейн. Их главная особенность заключается в том, что они не нуждаются в посредниках и обеспечивают прозрачность и безопасность транзакций.
Несмотря на все плюсы, смарт-контракты не застрахованы от ошибок и уязвимостей, что может серьезно навредить всем участникам. И вот здесь на сцену выходит аудит смарт-контрактов. Это полное исследование кода контракта с целью выявления и устранения потенциальных угроз безопасности и проверки его правильной работы. В перспективе аудит может:
- уберечь вас от потери денег,
- сохранить вашу репутацию;
- предотвратить возможные юридические споры о нарушении безопасности и других проблемах.
Проводить аудит должны сторонние эксперты, а не сами разработчики. Профессиональные аудиторы целенаправленно занимаются поиском уязвимостей и багов, которые разработчики могли пропустить. На выходе заказчик получает отчет и рекомендации по исправлениям.
Внесение изменений в смарт-контракт после аудита — вещь добровольная: мы лишь указываем на те места в протоколе, которые стоит скорректировать, а команда разработчиков уже думает, исправлять ей это или нет. Иногда мы спорим с командой заказчика, приводим аргументы, объясняем свою позицию, но ответственность за решение проблем в любом случае лежит на клиенте.
Зачем проводить аудит смарт-контрактов
- Гарантия функциональности. Непроверенный смарт-контракт — это потенциальный источник серьезных проблем. Он может банально не работать должным образом, и это повлияет на основные функции вашего продукта. Аудит смарт-контрактов поможет избежать этих неприятностей.
- Предотвращение проблем. Блокчейн, где хранится смарт-контракт, представляет собой непростую среду для обновлений. Как только смарт-контракт записан, в нем сложно что-то менять задним числом (именно это делает его таким надежным и одновременно добавляет головной боли разработчикам). Поэтому еще на этапе разработки крайне важно убедиться, что смарт-контракт полностью соответствует требованиям и имеет все необходимые функции.
- Обнаружение уязвимостей. Безопасность является огромной проблемой в мире смарт-контрактов, и пропущенная уязвимость — это зеленый свет хакеру украсть деньги из проекта. Это риск, который просто невозможно игнорировать.
Какие бывают виды аудита
Сразу оговорюсь, что аудит смарт-контрактов не является одноразовым мероприятием. Рынок и технологии постоянно меняются, поэтому аудит одного и того же проекта нужно проводить регулярно. При этом у каждой аудиторской компании свой метод обнаружения ошибок и угроз, и результаты аудита могут различаться. Лучше всего провести аудит усилиями нескольких — двух, трех — компаний, чтобы получить максимально детальную оценку и убедиться в полной проверке смарт-контракта.
Проверять смарт-контракты можно с помощью таких видов аудита:
- Инженерный аудит смарт-контракта.
Цель инженерного аудита — проверка функциональности, безопасности и соответствия требованиям смарт-контракта. В процессе такой проверки обычно анализируются технологическое решение контракта, его архитектура и логика работы. Аудитор ищет уязвимости, ошибки программирования и проблемы, которые могут привести к неправильной работе контракта, а в отчете пишет рекомендации по улучшению надежности и эффективности.
- Аудит экономических моделей смарт-контракта.
В этом типе аудита проверяются экономические параметры смарт-контракта. Аудитор анализирует формулы, распределение токенов или финансовых средств, оценивает правильность расчетов и устойчивость экономической модели. Тестирование обычно проводится с использованием имитационного моделирования на основе теории игр. В отчете содержатся рекомендации по улучшению экономических параметров и моделей смарт-контракта.
- Код-ревью смарт-контракта.
В этом случае аудиторы не вникают в логику проекта и работу его функций и смарт-контрактов, а формально оценивают качество написанного кода. Цель код-ревью — сделать код более читаемым и эффективным. Отчет по итогам, как правило, не составляется.
Код-ревью хорошо применять на этапе разработки, чтобы снизить дальнейшие затраты на аудит и по ходу улучшать качество итогового кода. Тогда аудиторы во время своей проверки сфокусируются только на обнаружении уязвимостей и потенциальных рисков в смарт-контрактах, а также возможных экономических атаках.
- Аудит безопасности смарт-контракта.
Это как раз то, чем занимаемся мы. Наша цель — проверить код контракта, криптографические методы, механизмы аутентификации и выявить потенциальные уязвимости, связанные с конфиденциальностью, целостностью и доступом к данным. По итогам проверки мы даем рекомендации по устранению уязвимостей, повышению уровня безопасности и защите смарт-контракта.
Как мы готовимся к аудиту смарт-контрактов
- Сначала обсуждаем с заказчиком конкретный скоуп для аудита: что будем проверять — один смарт-контракт или несколько связанных.
- Дожидаемся информации от разработчиков о том, что они применили финальный коммит: нам важно, чтобы во время аудита клиент не вносил никаких изменений.
- Утверждаем с заказчиком сроки аудита.
- Запрашиваем всю техническую документацию по проекту, включая кодовую базу, whitepaper, архитектуру.
- Глубоко погружаемся в продукт и документацию, чтобы в мельчайших деталях понимать, как он устроен и какие в нем могут быть слабые места. Только после этого мы можем приступить к аудиту.
Какие уязвимости мы ищем
Ошибки в смарт-контракте во многом зависят от стека вызовов. Поэтому уязвимости довольно трудно классифицировать. Однако у них есть общий знаменатель: все ошибки приводят к тому, что программа читает или записывает данные не по адресу памяти.
Я собрал небольшое меню наиболее распространенных блюд уязвимостей, которые встречались нам при проведении аудита смарт-контрактов.
Атака с повторным входом. При наличии такой уязвимости код выполняет одну функцию и в процессе своей работы вызывает вторую. Если вторая функция активируется до завершения первой, злоумышленник успеет вмешаться и изменить данные контракта.
Предположим, есть функции А и Б, где Б связана с деньгами или важными данными. При запуске функции А она не ждет, пока функция Б закончит свою работу. Мошенники могут запустить функцию А много раз, прерывая работу функции Б и изменяя данные контракта. Так, в 2016 году хакеры использовали такую уязвимость, чтобы взломать DAO и похитить деньги. Они успели несколько раз войти в систему и повторно выполнить атаку, прежде чем она успела защититься.
Арифметические переполнения и недополнения. Обычно возникают при написании смарт-контрактов на Solidity: основной язык Ethereum не обладает встроенными механизмами защиты от таких проблем.
Переполнение или недополнение происходят, когда смарт-контракт выполняет математическую операцию, дающую большее число, чем может быть сохранено в памяти. Это может привести к обнулению баланса и потере средств без возможности восстановления или, напротив, к тому, что контракт ошибочно начислит больше токенов, чем ожидалось. Если контракт отвечает за управление доступом к данным, злоумышленник может получить доступ к чувствительной информации или изменить права доступа других пользователей.
Зависимость от временной метки. Некоторые контракты используют block.timestamp— специальную переменную в блокчейне Ethereum, которая содержит временную метку текущего блока. Она представляет собой количество секунд, прошедших с начала эпохи UNIX (1 января 1970 года) до момента создания текущего блока. Смарт-контракты используют block.timestamp для доступа к текущему времени внутри сети Ethereum. Это время никому не принадлежит и устанавливается майнерами, которые добавляют транзакции в блоки и проверяют их.
Майнеры имеют возможность манипулировать значением block.timestamp, поскольку контролируют момент создания нового блока. Злоумышленники могут воспользоваться этим, чтобы атаковать контракт или нарушить его логику, используя разницу во времени.
Неправильный контроль доступа. Функции смарт-контрактов обычно имеют ограничения на вызов — например, только владелец контракта или избранные адреса могут использовать определенные функции.
Если административные функции контракта не защищены правильно или к ним имеют доступ неавторизованные пользователи, это может привести к взлому и потере средств или изменению параметров контракта. Происходит это как из-за недостаточной проверки участников, так и из-за отсутствия авторизации или ошибок в логике контракта.
Непроверенные возвращаемые значения. Многие стандартные функции в Solidity возвращают логическое значение true или false. Это значит, что после своего выполнения функция отчитывается либо об успешном выполнении задачи, либо об ошибке.
Однако, если эти возвращаемые значения не проверяются, контракт может продолжить свою работу даже после неудачного выполнения функции, и последствия могут быть самые разрушительные.
Как мы проводим аудит
Чтобы выявить и оценить уязвимости смарт-контракта, мы проводим ручные и автоматические тесты. Такие инструменты, как Mythril и Slither, могут обнаружить распространенные проблемы, но для выявления сложных уязвимостей, связанных с логикой, необходим ручной анализ. Люди видят даже те изъяны, которые правильно реализованы технически и успешно прошли автотест, но представляют собой слабое место для атаки. Каждый аудитор подходит к этой задаче по-своему, ведь не существует двух одинаковых специалистов.
Смарт-контракты также отличаются между собой. Даже если они основаны на одном и том же исходном коде, контракты могут иметь различия в архитектуре, логике, функциональности или используемых библиотеках. Это означает, что каждый контракт требует индивидуального подхода при аудите.
Тем не менее существуют базовые этапы, которые необходимо пройти в каждом аудите без исключения. Рассказываю о них:
- Анализ бизнес-логики. Аудитор начинает с понимания бизнес-логики и функциональности смарт-контракта. Для этого я обычно анализирую спецификации кода контракта и изучаю архитектуру. Я также могу задать вопросы команде разработчиков — например, о цели проекта или его объеме.
- Статический анализ. На этом этапе я проверяю каждую строку кода смарт-контракта. Я ищу потенциальные уязвимости, анализирую логику кода и убеждаюсь, что он соответствует лучшим практикам программирования. Также я проверяю, что код следует стандартным соглашениям, которые помогают обеспечить его качество, читаемость и защищенность.
- Динамический анализ. С помощью инструментов с вкусными названиями Truffle и Ganache я создаю контролируемую среду, в которой запускаю контракт и наблюдаю за его поведением. В процессе выполняю тесты на редкие и необычные сценарии использования контракта. Например, когда контракт получает неожиданные входные данные или значения переменных выходят за пределы обычного диапазона. Такие ситуации могут быть сложными для обработки или привести к непредвиденным результатам.
Здесь же я воссоздаю ситуации взлома контракта, изменения его данных или кражи средств.
- Анализ использования газа. Помимо поиска уязвимостей, аудитор анализирует расход газа контрактом, чтобы обеспечить его эффективность. Неоптимизированный контракт может использовать больше газа, чем необходимо, из-за чего транзакции будут стоить дороже. Высокие транзакционные издержки могут задерживать подтверждение операций или даже отменять их из-за недостатка газа. Вдобавок ко всему много газа вредит экологии.
- Создание отчета. По окончании аудита я создаю отчет, в котором рассказываю, какие уязвимости нашел, классифицирую их по степени серьезности проблем, которые каждая может вызвать (Critical, Major, Medium, Minor, Informational), и объясняю, что можно улучшить.
Как долго длится аудит смарт-контракта
Аудит смарт-контрактов требует времени и внимания к деталям. В среднем на аудит проекта из 500 строк и на подготовку рекомендаций по исправлению ошибок у нас уходит одна неделя. Понятно, что разработчикам неудобно ждать так долго, и они хотят приступить к работе над исправлениями как можно скорее.
Чтобы сэкономить время и деньги наших клиентов, мы разработали подход с интервальными отчетами. Вместо того чтобы ждать окончательного отчета, мы каждые 2–3 недели предоставляем команде разработчиков промежуточные результаты, в которых указываем найденные проблемы и предлагаем решения. Это позволяет им начать работу над исправлениями, не отходя от кассы не замораживая код.
У каждого аудитора есть свой уникальный стиль работы, набор знаний и опыт. Поэтому я всегда настаиваю на том, чтобы каждая область проекта независимо проверялась как минимум двумя аудиторами. Такой подход помогает выявить гораздо больше проблем и дает бóльшую уверенность в качестве аудита.
Что происходит после аудита
В день окончания аудита мы высылаем заказчику финальный отчет, в котором описаны все уязвимости. Философия блокчейна предполагает открытость и прозрачность, и публикация отчета позволяет проекту укрепить доверие пользователей. Обычно заказчики понимают это и разрешают нам опубликовать отчеты (посмотрите наш отчет для Lido V2). Чаще всего это происходит после устранения всех важных уязвимостей.
Некоторые отчеты не публикуются. Заказчик может захотеть непубличный аудит или попросить отложить публикацию, так как проект еще не запустился.
Дальше есть три основных варианта развития событий:
- Мы не нашли серьезных уязвимостей в контрактах. В этом случае заказчик смотрит отчет, комментирует каждый пункт, мы обновляем отчет с учетом комментариев и закрываем проект.
- Мы нашли critical или major уязвимости. В такой ситуации мы считаем важным проверить, как были устранены уязвимости и не появилось ли новых. Для этого мы договариваемся с заказчиком о повторной проверке — реаудите. Сроки исправления составляют от одной недели до нескольких месяцев, но, как правило, заказчик сам заинтересован как можно быстрее всё пофиксить, чтобы не откладывать релиз проекта.
- После аудита выясняется, что заказчик внес существенные изменения в контракты. Тут реаудит уже не поможет — нужно заново провести полноценный аудит, чтобы посмотреть, как всё работает в изменившихся условиях. В нашей практике бывало, что проект проходил полный аудит несколько раз: из-за новых изменений мы не могли взять его на реаудит. Поэтому мы так топим за то, чтобы заказчик отдавал на аудит финальную версию смарт-контракта.
Иногда после аудита мы предлагаем команде разработчиков нашу техническую поддержку. Заказчику удобно устранять уязвимости с нашей помощью: мы глубоко разобрались в продукте и понимаем, как это сделать. Мы проверяем, как разработчики исправили ошибки, консультируем их в процессе устранения проблем, потому что стараемся быть не просто аудиторами, а партнерами для наших клиентов.
И еще пара слов об аудите смарт-контрактов
Блокчейн — это про доверие. Пользуясь блокчейн-приложениями, мы ждем, что наши активы будут надежно защищены.
В этом смысле аудит смарт-контрактов — это не просто формальная проверка. Это основательное исследование, в ходе которого независимые эксперты анализируют код и логику работы. Они ищут потенциальные ошибки, уязвимости и риски, чтобы устранить их до того, как они могут повлиять на работу проекта и сохранность средств.
Открытый отчет по аудиту становится маркером надежности проекта. Люди думают: «Раз есть аудит, значит, кто-то его проверил» и верят в то, что приложением можно безопасно пользоваться.