crypto-folio

Понятно, практично, по делу

Вопросы и ответы·12 сентября 2026 г.·12 мин

Безопасность смарт-контрактов: ключевые этапы и факторы аудита

Как понять, что смарт-контракт действительно готов к запуску, а не просто успешно компилируется и проходит несколько тестов? Короткий ответ: по одному признаку — например, по наличию отчёта аудиторской компании — этого определить нельзя.

Безопасность смарт-контрактов: ключевые этапы и факторы аудита

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

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

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

С чего начинается аудит: границы проверки и модель угроз

Первый этап аудита смарт-контрактов часто называют scoping — определением области проверки. На этом шаге фиксируют, какие именно контракты, библиотеки, функции и версии кода должны попасть в аудит.

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

Обычно на старте нужно разобраться как минимум в следующих вопросах:

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

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

Для DeFi-протокола недостаточно спросить, может ли обычный пользователь вызвать функцию вывода средств. Нужно дополнительно проверить, что произойдёт при необычном порядке вызовов, резком изменении цены актива, возврате ошибки внешним контрактом или повторном обращении к системе до завершения первой операции.

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

Хороший аудит начинается не со сканера, а с понимания того, что именно должен делать протокол и какие последствия будет иметь ошибка.

Как устроен жизненный цикл аудита

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

1. Определение области аудита

Команда передаёт аудиторам исходный код, документацию, описание архитектуры и правила, по которым должен работать протокол. На этом же этапе фиксируются версии файлов и перечень компонентов, которые входят в проверку.

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

2. Анализ архитектуры и модели угроз

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

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

3. Автоматизированное тестирование

Инструменты быстро просматривают код и помогают найти распространённые опасные конструкции, нарушения шаблонов безопасности и подозрительные участки. Такой анализ полезен для первичного охвата большого объёма кода, но он не понимает бизнес-логику протокола так, как её понимает человек.

4. Ручной анализ

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

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

  • микроаудит — проверку отдельных функций, условий, расчётов и механизмов контроля доступа;
  • макроаудит — изучение взаимодействия функций и контрактов на уровне всей системы.

5. Первичный отчёт и исправление ошибок

Найденные проблемы классифицируют по уровню серьёзности. В отчётах обычно встречаются категории Critical или High, Medium, Low, а также Informational и Gas Optimization.

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

6. Повторная проверка

После исправлений аудит не должен заканчиваться автоматически. Разработчики могут устранить одну проблему и случайно изменить поведение соседней функции. Поэтому проводится fix verification — проверка исправлений.

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

Что дают Slither, Mythril и Echidna

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

Инструмент или подходМетод работыЧто даёт аудиторуОграничения
SlitherБыстрый статический анализ исходного кодаНаходит подозрительные шаблоны и распространённые ошибки за секундыНе моделирует всю бизнес-логику и не заменяет ручную проверку
MythrilСимвольное выполнение байт-кода EVMИсследует возможные пути исполнения и условия, при которых возникает проблемаАнализ может занимать минуты или часы, а сложные сценарии требуют интерпретации специалиста
EchidnaФаззинг, то есть подача множества псевдослучайных входных данныхПроверяет, как контракт ведёт себя на необычных комбинациях вызовов и значенийРезультат зависит от заданных свойств и качества подготовленных тестов
Ручной ревьюПострочное и архитектурное изучение кодаПроверяет смысл операций, роли, допущения и взаимодействие компонентовТребует времени и опытного аудитора

Slither: быстрый первый проход

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

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

Поэтому результат Slither — это карта участков для дальнейшего изучения, а не окончательный вердикт.

Mythril: поиск возможных путей исполнения

Mythril применяет символьное выполнение байт-кода EVM. Вместо проверки только конкретных значений он пытается рассуждать о возможных состояниях переменных и путях исполнения.

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

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

Echidna: неожиданные входы и поведение контракта

Фаззинг — это тестирование с большим количеством автоматически сгенерированных входных данных. Echidna использует такой подход, чтобы проверить, сохраняются ли заданные свойства контракта при разнообразных последовательностях вызовов.

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

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

Почему ручной анализ остаётся центральным

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

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

Именно поэтому ручной ревью должен отвечать не только на вопрос «есть ли ошибка в этой строке», но и на более широкий вопрос: «может ли пользователь заставить систему перейти в состояние, которое разработчики не предусматривали?»

Микроаудит: что происходит внутри функции

На микроуровне специалист изучает:

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

Здесь особенно важна последовательность операций. Даже если каждая строка сама по себе выглядит нормально, неправильный порядок действий может открыть путь к атаке.

Макроаудит: как функции влияют друг на друга

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

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

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

Reentrancy: старая угроза, которая всё ещё требует внимания

Атака повторного входа, или reentrancy, стала одной из самых известных классических уязвимостей в экосистеме Ethereum. Её суть в том, что контракт выполняет внешний вызов до того, как обновит собственное внутреннее состояние.

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

История The DAO в 2016 году сделала проблему особенно известной, но сам принцип важен не только как исторический пример. Он показывает, почему аудитору приходится изучать порядок операций и доверительные границы между контрактами.

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

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

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

Формальная верификация: когда код проверяют математически

Формальная верификация — более строгий подход к анализу смарт-контрактов. Её задача — математически доказать соответствие кода заданной спецификации, то есть набору правил, которым контракт должен следовать.

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

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

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

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

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

Как оценивать отчёт об аудите

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

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

Также стоит посмотреть:

1. Какие проблемы нашли. Наличие замечаний само по себе не является признаком плохого проекта. Гораздо важнее, как команда на них отреагировала.

2. Исправлены ли уязвимости. В хорошем процессе есть отдельная проверка исправлений, а не только первоначальный список недостатков.

3. Есть ли ограничения аудита. Аудиторы могут не проверять сторонние зависимости, экономическую модель или интерфейс взаимодействия с внешним протоколом.

4. Какие методы использовали. Автоматический анализ полезен, но он не заменяет ручной ревью.

5. Совпадает ли проверенный код с развёрнутым. Это один из самых практичных вопросов, который часто забывают задать.

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

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

Что в итоге определяет безопасность смарт-контракта

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

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

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

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

Частые вопросы

Почему автоматического сканирования кода недостаточно для безопасности?
Автоматические инструменты ищут известные шаблоны уязвимостей, но не понимают бизнес-логику протокола и не могут оценить, как функции взаимодействуют друг с другом в рамках всей системы.
Что такое scoping и почему это важно для аудита?
Scoping — это определение границ проверки, включая перечень контрактов, функций и версий кода. Это важно, чтобы аудиторский отчёт соответствовал именно той версии программного обеспечения, которая будет развёрнута в блокчейне.
В чем разница между микроаудитом и макроаудитом?
Микроаудит фокусируется на проверке отдельных функций, условий и расчётов, тогда как макроаудит изучает взаимодействие функций, ролей и контрактов на уровне всей архитектуры системы.
Что такое атака повторного входа (reentrancy)?
Это уязвимость, при которой контракт выполняет внешний вызов до обновления собственного внутреннего состояния, что позволяет злоумышленнику повторно обратиться к функции и нарушить логику работы, например, вывести средства несколько раз.
Заменяет ли формальная верификация обычный аудит?
Нет, формальная верификация лишь усиливает проверку, математически доказывая соответствие кода заданным правилам. Она не отменяет необходимость архитектурного анализа и ручного аудита, так как не учитывает все внешние зависимости и интеграции.
Текст: Олег Сафонов, Проводник в мир Web3 для новичков