Аудит смарт-контрактов: защита DeFi от уязвимостей

Перед запуском DeFi-протокола ошибка в логике обходится дороже любой задержки. Мы разбираем смарт-контракты вручную и машинными проверками, снимаем целые классы отказов и передаём команде понятный отчёт с приоритетами правок. Работаем под ключ: от разбора кода до регламента повторных проверок.

Направления блокчейн-разработки

Часто задаваемые вопросы

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1530
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1354
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1070
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1328
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    772
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1074

Аудит смарт-контрактов: где логика ломается до первого взлома

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

Первая типовая точка — право вывода средств у одной роли. Если единственный адрес может забрать всю ликвидность без задержки, лимитов и второй подписи, цена ошибки в ключах равна цене всего пула. Такие права дробим на роли, добавляем очередь на исполнение и потолок разового вывода.

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

Третья — внешние вызовы без проверки результата: возвращённый отказ, устаревшая цена, пустой ответ проходят как успех, и протокол фиксирует сделку по неверным данным. Защита DeFi от взлома начинается с проверки каждого внешнего ответа и ограничения его по времени.

Базовые примитивы берём из проверенных библиотек — OpenZeppelin — и не пишем собственную арифметику там, где она уже прошла публичную проверку. Дальше разбираем, как эти уязвимости смарт-контрактов закрываются до релиза и что остаётся в отчёте.

Что меняется после проверки: снятые классы отказов и скорость правок

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

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

Скорость выпуска правок меняется качественно: найденный сценарий атаки превращается в регрессионный тест, и исправленная версия проверяется повторным прогоном всего набора, а не ручным пересмотром изменённых строк. Путь от находки до версии с исправлением укладывается в один цикл правок вместо серии итераций «нашли — переписали — нашли снова».

Предсказуемость релизов держится на жёстком правиле: развёртывание не проходит при открытой критичной находке. Чек-лист проверок встроен в конвейер сборки, отчёт фиксирует статус по каждому классу уязвимостей, и решение о выпуске принимается по факту закрытия рисков, а не по ощущению готовности команды.

Аудит смарт-контрактов: ручной разбор и машинные проверки

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

Форматы проверки отличаются по глубине и по тому, какой класс ошибок они вообще способны увидеть:

Вариант проверки Когда уместен Ограничения
Ручной разбор кода Логика протокола, экономика, права ролей, связки с внешними контрактами Требует времени и квалификации, покрытие зависит от выбранных путей
Статический анализ контрактов Первый проход по всей кодовой базе, поиск типовых уязвимостей, проверка в сборочном конвейере Много ложных срабатываний, не видит логических ошибок
Формальная верификация Критичные инварианты: сохранность балансов, лимиты выпуска, условия ликвидации Нужна формальная спецификация; применима к ядру, а не ко всей системе
Повторная проверка после правок Любое исправление, затрагивающее хранение состояния или внешние вызовы Проверяет изменённые участки, требует оценки влияния на остальную систему

На практике подходы идут связкой: машинный проход по всему репозиторию, ручной разбор критичных путей, формальная верификация ядра и повторная проверка после каждой правки. Так закрываются и типовые ошибки, и логические дефекты, которые инструмент не увидит в принципе.

Как проходит внедрение: от разбора кода до передачи отчёта

Проверка идёт последовательными этапами: сначала фиксируем, что контракт обязан делать, затем — как он может сделать не то. Без первых шагов аудит сводится к прогону готового набора правил, и часть путей атаки остаётся вне разбора.

  1. Сбор требований и границ. Уточняем логику, роли, места хранения средств и внешние вызовы; выписываем инварианты, которые нельзя нарушать ни при каких условиях.
  2. Схема контракта и ролей. Карта вызовов, прав и зависимостей между модулями показывает, где один участник может задеть чужие средства.
  3. Построчный разбор. Идём по схеме через код: арифметика и округления, проверки доступа, порядок обновления состояния, работа с внешними вызовами.
  4. Прогон сценариев атаки. Повторное вложение вызовов, манипуляция ценой, обгон транзакций, вывод сверх баланса — каждую гипотезу подтверждаем тестом, а не рассуждением.
  5. Исправление. С вашей командой вносим правки или отдаём точечные патчи, отмечая, где меняется логика, а не только защита.
  6. Контрольная проверка. Перепрогоняем сценарии и убеждаемся, что патч не открыл новый путь и не сломал инварианты из первого пункта.
  7. Передача отчёта. Находки с уровнем риска, путём воспроизведения и статусом исправления; при смене логики — повторная проверка после вывода в сеть.

Каждый этап оставляет след: видно, что проверено, что осталось и на чём основан вывод.

Что остаётся в проекте: конфигурации, схемы и регламенты

Аудит заканчивается не вердиктом «уязвимостей не найдено», а комплектом артефактов, с которым команда дальше работает без нас: ниже — что остаётся в репозитории после сдачи проверки.

  • Отчёт о проверке — по каждому замечанию класс риска, путь исполнения, ссылка на функцию и минимальный сценарий воспроизведения. Формулировок «рекомендуем проверить» нет: либо конкретный вызов, либо пункт в отчёт не попал.
  • Артефакты проверки — конфигурации статического анализа, сценарии фаззинга, инвариантные тесты и срезы покрытия. Комплект перезапускается одной командой, поэтому результат воспроизводим.
  • Схемы доступа — карта ролей: кто выпускает активы, меняет параметры, замораживает операции и обновляет логику; роли с задержкой исполнения и кандидаты под мультиподпись отмечены отдельно.
  • Политика изменений — какие правки требуют повторной проверки, а какие идут по упрощённому порядку; правила обновления логики через прокси и запрет на смену порядка полей в хранилище.
  • Регламент повторных проверок — триггеры: правка расчётов, смена экономических параметров, расширение прав администратора, подключение внешнего источника данных.
  • Документация по контракту — инварианты, событийная модель для мониторинга, перечень внешних вызовов и допущений, на которых держится логика.
  • Журнал принятых рисков — замечания, осознанно оставленные без правок, с обоснованием и оценкой последствий: новой команде не придётся выяснять это заново.

Комплект снимает зависимость от подрядчика: следующую проверку можно провести своими силами или передать другой команде — точка входа одна.

Разбор случая: до и после устранения критичной уязвимости

Что было до исправления

Кейс проверки контракта начинался с двух аудитов у прежних подрядчиков: контракт собирался, статические анализаторы давали чистый отчёт. В логике вывода средств оставался повторный вызов — внешний контракт успевал вернуть управление раньше, чем обновлялся внутренний баланс. Класс описан в SWC Registry как взаимодействие до изменения состояния — ни один сканер его здесь не подсветил, потому что уязвимость проявлялась только в связке вызовов, а не внутри одной функции.

Что нашли и что изменили

Инженеры прошли поток вызовов от ввода средств до вывода и свели его в схему изменения балансов — на ней стало видно окно между списанием и записью. Сценарий инцидента с ликвидностью воспроизвели на локальной копии сети: серия вызовов вытягивала из пула больше, чем в него внесли, и делала это без единой ошибки в логах. Исправление свели к порядку «проверка — запись — внешний вызов», добавили защиту от повторного входа и регрессионный тест, который падает при возврате старого порядка операций.

Показатель До После
Реакция на аномалию в пуле часы минуты
Доля путей вывода средств под тестами 40% 95%
Обнаружение класса «повторный вызов» не выявлялся закрыт тестом

После устранения критичной уязвимости повторная проверка не нашла новых путей оттока, а мониторинг пула перевели на пороговые оповещения по объёму резервов. Откат на предыдущую версию контракта остался доступен — правки внесли так, чтобы состояние пула не требовало миграции.

Чем проверка перед запуском отличается от разбора после инцидента?

Проверка перед запуском — это работа с исходным кодом до того, как в контракте появились деньги. Инженеры идут по всем ветвям исполнения: права администратора, внешние вызовы, порядок обновления состояния, зависимости от внешних источников данных. Отдельно моделируются экономические сценарии — манипуляция курсом, вывод ликвидности, разгон стоимости токена.

Разбор после инцидента решает другую задачу: остановить потери и понять вектор. Порядок обратный — сначала следы транзакций в сети, потом восстановление последовательности вызовов, и только затем причина в коде. Если исходники недоступны или развёрнутый код отличается от них, анализировать приходится по байткоду.

Разница подходов — в целях, а не в инструментах. До запуска нужна полнота: покрыть все пути исполнения, описать классы риска и условия их реализации. После инцидента нужна скорость: два-три узла, через которые прошёл вывод средств, срочные меры — пауза, миграция, обновление контракта — и только затем полный разбор случившегося.

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

Частые технические сомнения перед началом работ

Сомнения перед аудитом редко касаются методологии — чаще спрашивают про границы работы: что реально проверяется в уже выложенном коде, где заканчивается автоматика и начинается ручной разбор, кто отвечает за находки после передачи. Ниже — ответы на то, что звучит на первом разборе.

Контракт уже развёрнут — проверять поздно?

Нет, хотя правки дороже. Сверяем код в сети с исходником, смотрим, обновляется ли логика через прокси и у кого права на изменение. Если дыра закрывается обновлением — готовим патч, если нет — считаем перенос состояния.

Автоматических анализаторов достаточно?

Нет. Они ловят шаблонные ошибки: обход проверок доступа, переполнения, неинициализированные переменные. Логику протокола так не увидеть — её разбираем вручную и прогоняем сценариями на копии сети.

Проверяете ли экономическую логику?

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

Что после правок по вашему отчёту?

Повторная проверка изменённых участков, а не всего кода заново: подтверждаем, что закрыты именно найденные пути и не появилось новых. Регрессионный набор тестов остаётся у команды.

Что с ответственностью после передачи?

Передаём отчёт с уровнями риска, тесты и схемы вызовов; ключами не владеем и протоколом не управляем. Отвечаем за методологию и воспроизводимость выводов, поэтому повторные проверки после каждого существенного обновления закладываем заранее.

Обсудим задачу: что уточнить перед началом проверки

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

Что помогает обсудить задачу предметно:

  • Границы проверки: какие контракты в периметре, а какие внешние вызовы — оракулы, мосты, пулы ликвидности — считаем доверенными и не разбираем.
  • Схема ролей и доступов: у кого админ-функции, есть ли мультиподпись и задержка на критичные операции вроде вывода средств.
  • История изменений и исходный код: как сопоставить репозиторий с развёрнутым в сети контрактом, чтобы логика совпадала со сборкой.
  • Известные инциденты и допущения: что уже находили сами, что осознанно оставили как есть, какие места команда считает рискованными.
  • Требования регуляторов и площадок: формат заключения, который примут партнёры или торговая площадка, и нужные разделы отчёта.
  • Сроки запуска или следующего релиза: от даты зависит порядок работ — сначала критичные пути, затем остальная логика.
  • Экономика протокола: как считаются доли, комиссии и ликвидации — расхождения в формулах чаще всего и открывают путь к потере средств.

После этого согласование объёма занимает один разговор: фиксируем, что входит в проверку, что остаётся за периметром, и в каком порядке выдаём результаты — по критичным путям раньше, чем по периферии.