Проверка смарт‑контракта на уязвимости - это сочетание ручного ревью кода, статического анализа (SAST), динамических тестов (фуззинг/символьное исполнение) и аудита зависимостей/конфигурации. Практичный порядок: собрать артефакты и воспроизводимую среду, прогнать несколько сканеров, подтвердить находки тестами и сценариями атак, затем оценить риск и составить план исправлений.
Краткая сводка рисков и ключевых выводов
- Не полагайтесь на один инструмент: разные анализаторы находят разные классы проблем и дают ложные срабатывания.
- Сначала обеспечьте воспроизводимость сборки (компилятор, оптимизации, зависимости), иначе результаты будут недостоверны.
- Критичность определяется не "страшностью" отчёта, а достижимостью в атаке и влиянием (потеря средств, блокировка, захват прав).
- Динамические проверки обязаны подтверждать эксплуатацию (PoC) на форке/тестнете, а не на проде.
- Проверяйте не только код: конфиги деплоя, роли, прокси‑паттерны, параметры компилятора и зависимости часто опаснее.
Подготовка окружения и сбор артефактов для проверки
Кому подходит: разработчикам и ревьюерам, которые могут собрать проект локально и запустить тесты, а также командам, готовящим релиз/апгрейд или интеграцию с внешними протоколами.
Когда не стоит делать самостоятельно: если контракт уже управляет значимыми активами, есть сложные кроссчейн/мультипротокольные взаимодействия, или вы не можете воспроизвести сборку/тесты - лучше привлекать независимый аудит и ограничить изменения (минимальный патч + защитные лимиты).
Что собрать заранее (артефакты)
- Исходники контрактов и точный список версий компилятора Solidity (pragma + фактическая версия solc).
- Конфигурации сборки: Hardhat/Foundry/Truffle, параметры оптимизатора, remappings, настройки viaIR (если используются).
- ABI, bytecode, адреса прокси/реализаций (если уже деплоилось), список ролей/админов, параметры инициализации.
- Тесты, сценарии деплоя/миграций, скрипты апгрейда, адреса внешних зависимостей (оракулы, токены, роутеры).
- Для верификации на форке: RPC-эндпоинт, номер блока, состояние (список ключевых аккаунтов/балансов).
Быстрый старт окружения (пример)
- Foundry:
curl -L https://foundry.paradigm.xyz | bash, затемfoundryup - Hardhat:
npm i, затемnpx hardhat test - Локальная сеть: Anvil
anvilили Hardhat Networknpx hardhat node - Форк сети (только для тестов):
anvil --fork-url $RPC_URL --fork-block-number $BLOCK
Анализ исходного кода: чек‑лист типичных уязвимостей
Что понадобится: доступ к репозиторию, возможность собрать проект (npm/pnpm + solc/forge), прогнать тесты, а также понимание модели угроз (кто атакующий, какие активы, какие привилегии и точки входа). Желательно иметь сценарии использования (user stories) и список инвариантов протокола.
Чек‑лист ручного ревью (то, что чаще всего пропускают)

- Контроль доступа: корректность ролей, модификаторов, owner/admin, отсутствие "забытых" публичных сеттеров, защита инициализаторов в прокси.
- Внешние вызовы: reentrancy, вызовы в untrusted контракты, порядок "checks-effects-interactions", опасные callback‑механики (ERC777/ERC1155 hooks).
- Математика и единицы: rounding/precision, несоответствие decimals, переполнение/underflow в кастомных библиотеках, деление до умножения.
- Логика времени и блоков: зависимости от
block.timestamp/block.number(манипулируемы в пределах), окна обновления цен/периодов. - Оракулы и цены: отсутствие проверки stale/heartbeat, уязвимость к манипуляции DEX‑ценой, некорректные TWAP/spot.
- MEV и фронт‑ран: публичные функции, позволяющие перехватить выгодное состояние, отсутствие commit‑reveal там, где нужно.
- DoS: циклы по динамическим массивам, push‑механики выплат, зависимость от того, что внешний вызов "всегда успешен".
- Upgradeability: storage layout, gap, коллизии слотов, отсутствие
onlyProxy/initializer, неверный admin у прокси. - Права на токены: опасные
approve/permit, неочевидные allowances, fee-on-transfer/deflationary токены. - События и мониторинг: отсутствие событий на критичных изменениях усложняет реагирование и расследование.
Статический анализ и подбор инструментов сканирования
Риски и ограничения перед запуском сканеров
- Сканер может "найти" проблему, которой нет: подтверждайте находку тестом/PoC и трассировкой.
- Инструменты зависят от корректной сборки: неверная версия solc, remappings или оптимизации меняют результат.
- Результаты часто неполные для прокси/апгрейдов: анализируйте реализацию, инициализаторы и сценарий апгрейда.
- Нельзя безопасно проверять эксплуатацию на mainnet: используйте локальную сеть, форк или тестнет.
Таблица выбора инструментов: точность, скорость, ложные срабатывания
| Инструмент | Тип | Сильные стороны | Скорость | Ложные срабатывания | Когда выбирать |
|---|---|---|---|---|---|
| Slither | SAST (быстрые правила/анализ графов) | Широкое покрытие паттернов, удобные отчёты, быстрый прогон | Высокая | Средние | Базовый "первый проход" на любом проекте |
| Mythril | Символьный анализ | Ищет глубже логические баги и пути до ошибок | Низкая-средняя | Средние | Когда важны сложные пути выполнения и условия |
| Echidna | Фуззинг свойств (property-based) | Находит реальные контрпримеры при хороших инвариантах | Средняя | Низкие при корректных свойствах | Когда есть чёткие инварианты и тестовая инфраструктура |
| Manticore | Символьное исполнение | Глубокий поиск по путям, полезен для PoC | Низкая | Низкие-средние | Точечная проверка подозрительных участков |
| Foundry (forge) | Тестирование/форк/инварианты | Быстрые тесты, форк mainnet, удобная трассировка | Высокая | Зависит от тестов | Подтверждение эксплуатации и регрессии после фикса |
Пошаговая инструкция (SAST + базовая валидация)
-
Зафиксируйте сборку и компилятор
Соберите проект "с нуля", убедитесь, что зависимости подтягиваются детерминированно, а версия solc совпадает с ожидаемой. Сохраните параметры оптимизатора и включение viaIR, если применимо.
- Foundry:
forge --version,forge build -vvv - Hardhat:
npx hardhat compile
- Foundry:
-
Прогоните Slither как быстрый скрининг
Slither быстро подсветит типовые паттерны: reentrancy‑риски, неинициализированные переменные, подозрительные вызовы, нарушения best practices. Начните с отчёта по всему проекту, затем уточняйте настройки.
- Пример:
slither . - Если проект на Hardhat:
slither . --hardhat-artifacts-directory artifacts
- Пример:
-
Проверьте подозрительные места символьным анализом
Используйте Mythril (или Manticore) не "на всё подряд", а на критичные контракты и функции: управление ролями, вывод средств, расчёт долей, апгрейды. Это экономит время и снижает шум.
- Mythril:
myth analyze contracts/MyContract.sol
- Mythril:
-
Сведите результаты в единый трекер находок
Для каждой находки заведите карточку: описание, затронутые функции, условия эксплуатации, предполагаемое влияние, ссылка на конкретные строки/коммиты. Отдельно отмечайте "подтвердилось тестом" и "похоже на ложное".
-
Подтвердите минимум одну критичную гипотезу тестом на форке
До глубокого фуззинга выберите 1-2 наиболее рискованных сценария и проверьте их воспроизводимость на локальной сети/форке. Это быстро показывает реальный профиль угроз и помогает правильно приоритизировать остальные проверки.
- Foundry:
forge test -vvvи тесты с RPC‑форком через переменные окружения - Hardhat:
npx hardhat test+ настройка forking вhardhat.config
- Foundry:
Динамическое тестирование: фуззинг, символьное исполнение и сценарии атак
Цель динамики - не "получить больше алертов", а доказать достижимость: контрпример для инварианта, PoC для уязвимости, трасса вызовов и минимальный набор условий.
Чек‑лист проверки результата (что должно быть в конце)
- Для каждой критичной/высокой находки есть воспроизводимый сценарий: тест или скрипт, который падает до фикса и проходит после.
- Эксплуатация проверена на локальной сети или форке с зафиксированным блоком и входными данными.
- Инварианты сформулированы явно (например: "сумма долей не превышает 1", "баланс пула не уходит в минус", "роль admin не меняется без события/кворума").
- Фуззинг покрывает границы: нули, максимумы, повторные вызовы, последовательности вызовов, случайные адреса токенов (включая fee-on-transfer).
- Есть тесты на reentrancy (в т.ч. через вредоносный контракт‑реентрантер), а не только проверка модификатора.
- Проверены сценарии отказа внешних зависимостей: revert/return false/пауза оракула/стейл‑данные.
- Для апгрейдов есть тест: до/после апгрейда значения storage сохранены, инициализаторы не переиспользуются.
- Для MEV‑рисков есть сценарии "передвинули порядок транзакций", "подменили цену на один блок", "сэндвич вокруг свопа".
Минимальные команды/подходы для динамики
- Foundry инварианты: пишите handler‑контракт и запускайте
forge test --match-test invariant -vvv. - Echidna: задайте свойства в Solidity и прогоняйте
echidna-test contracts/MyProps.sol --contract MyProps. - Символьное исполнение (точечно): берите одну функцию/контракт и ограничивайте пространство ввода, иначе анализ будет слишком долгим.
Аудит зависимостей, библиотек и конфигураций развертывания
Проблемы часто возникают на стыке: несовместимые версии библиотек, неправильная инициализация, ошибочные адреса в прод‑конфиге, неверная модель прав в прокси. Проверьте это так же строго, как и Solidity‑код.
Частые ошибки, которые приводят к инцидентам
- Непинованные зависимости: плавающие версии пакетов/подмодулей, разные сборки у разных разработчиков.
- Смешение библиотек и компиляторов: разные minor‑версии solc для разных модулей, неучтённые изменения поведения оптимизатора.
- Неправильные адреса внешних контрактов: роутер/оракул/токен не той сети, подменённый адрес в конфиге.
- Отсутствие проверок на chainId/домены: подписи/permit/кроссдоменная логика без доменного разделения.
- Proxy‑конфигурация: админ прокси у EOA без защиты, неверный порядок деплоя, повторный вызов initializer, открытые функции апгрейда.
- Роли и мультисиг: доступ к паузе/апгрейду/параметрам у неподходящих аккаунтов, нет задержки/процесса изменения.
- Параметры компиляции: разные флаги оптимизатора между аудитом и прод‑деплоем, несоответствие верификации.
- Секреты и ключи: приватные ключи/сид‑фразы в репозитории, CI‑логах, переменных окружения без ограничений.
- Неполные миграции: забытые вызовы инициализации, неустановленные лимиты, пустые адреса получателей комиссий.
Оценка риска, приоритизация найденных проблем и план исправлений
Приоритизируйте по достижимости и влиянию: какие активы под угрозой, какие роли нужны атакующему, можно ли автоматизировать атаку, как быстро она масштабируется. Результат должен быть планом работ с тестами регрессии и критериями готовности.
Практичная схема приоритизации
- Влияние: потеря/блокировка средств, захват прав, нарушение инвариантов протокола, деградация доступности.
- Достижимость: публичная функция vs требуется роль, один вызов vs сложная последовательность, нужен ли MEV/манипуляция оракулом.
- Детектируемость и обратимость: можно ли остановить (pause), есть ли лимиты, возможен ли откат/апгрейд без ущерба.
- Поверхность: сколько точек входа и интеграций затронуто, есть ли аналогичные места в коде.
Варианты действий (когда что уместно)

- Точечный фикс + регрессионные тесты: уместно, когда баг локализован и легко покрывается тестом/инвариантом; обязательно добавьте тест, который воспроизводит проблему.
- Защитные ограничения без изменения бизнес‑логики: уместно, когда нужен быстрый risk‑reduction (лимиты, пауза, allowlist, rate‑limit, cap) до полноценного рефакторинга.
- Рефакторинг модуля/архитектуры: уместно при системной причине (неверная модель прав, опасные внешние вызовы, смешение ответственности) - дороже, но снижает класс рисков.
- Ограничение фичи/вывод из эксплуатации: уместно, когда исправление рискованнее или сложнее, чем отключение компонента, и есть безопасный обходной путь.
Практические ответы на часто встречающиеся сложности
Можно ли ограничиться одним Slither и считать контракт проверенным?
Нет: Slither хорош как быстрый скрининг, но логические баги и сложные пути часто требуют символьного анализа и динамических тестов. Минимум - Slither + тесты на форке + один инструмент для глубокой проверки подозрительных мест.
Что делать, если сканер выдаёт много ложных срабатываний?
Сведите находки в трекер и подтверждайте приоритетные пункты тестом/PoC. Для каждого правила фиксируйте контекст: почему это безопасно именно в вашем коде, чтобы не возвращаться к тому же спору.
Как безопасно проверять эксплуатацию, не рискуя реальными средствами?
Запускайте проверки на локальной сети или на форке с фиксированным блоком и тестовыми аккаунтами. Никогда не проводите "эксплуатационные эксперименты" в mainnet и не используйте реальные ключи.
Как проверить контракт с прокси (UUPS/Transparent), если анализатор "не видит" логику?
Анализируйте контракт реализации и отдельно сценарий инициализации/апгрейда. Обязательно проверьте storage layout и защиту initializer, иначе даже корректная логика может быть скомпрометирована.
Нужно ли писать инварианты, если уже есть обычные unit‑тесты?
Да, инварианты ловят неожиданные последовательности вызовов и крайние значения, которые unit‑тесты часто не покрывают. Начните с 3-5 инвариантов вокруг активов, ролей и лимитов.
Как понять, что уязвимость действительно критичная?
Критичная - та, что достигается атакующим с реалистичными правами и приводит к потере/заморозке средств или захвату управления. Если без привилегий можно воспроизвести PoC на форке - приоритет должен быть максимальным.



