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

Атака повторного входа: почему Смарт-контракты продолжают истощаться ( и как это остановить )

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

Вот жестокая правда: Более $100M было потеряно из-за эксплойтов повторного входа. Самый известный? Взлом DAO (2016) украл $50M в ETH, используя именно эту уязвимость.

Как работает атака (Используя реальную логику кода)

Представьте, что ContractA владеет 10 ETH, а ContractB имеет баланс в 1 ETH, хранящийся внутри него.

Когда ContractB вызывает withdrawAll(), вот что должно произойти:

  1. Проверьте баланс > 0 ✓
  2. Отправить ETH обратно ✓
  3. Обновить баланс на 0 ✓

Но вот где это ломается: нападающий использует порядок операций.

Поток эксплуатации:

  • Атакующий вызывает attack() → который вызывает withdrawAll() на ContractA
  • ContractA отправляет 1 ETH и вызывает резервную функцию атакующего ()
  • Перед обновлением баланса до 0, fallback() немедленно снова вызывает withdrawAll()
  • ContractA проверяет: “Баланс > 0?” ДА (потому что он еще не обновился!)
  • Отправляет еще 1 ETH → снова вызывает fallback()
  • Этот цикл продолжается, пока ContractA полностью не опустошится

Ключевое понимание: Обновление баланса происходит ПОСЛЕ перевода ETH. Это окно уязвимости.

Три стратегии обороны

1. Модификатор nonReentrant (Защита единственной функции)

Заблокируйте функцию во время её выполнения. Повторный вход не допускается: солидность модификатор nonReentrant { require(!locked, “Нет повторного входа”); заблокировано = true; _; locked = false; }

Просто, но защищает только одну функцию за раз.

2. Шаблон Проверок-Эффектов-Взаимодействий (Многофункциональная защита)

Это меняет правила игры:

  • Проверки: Подтвердите условия (баланс > 0)
  • Эффекты: Обновить состояние (баланс = 0) ← Переместите это ПЕРЕД отправкой ETH
  • Взаимодействия: Отправить ETH

Неверный заказ:

требуется(баланс > 0); → отправить ETH → баланс = 0; // Слишком поздно!

Правильный порядок:

требуется(баланс > 0); → баланс = 0; // Обновить ПЕРВЫЙ → отправить ETH // Затем взаимодействовать

Теперь, даже если fallback() снова войдет, баланс уже 0. Атака провалилась.

3. GlobalReentrancyGuard ( Защита Межконтрактов )

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

Почему это важно

Рецидив не является проблемой только для Solidity — это проблема дизайна. Каждый раз, когда вы отправляете ETH или вызываете внешние функции, вы передаете контроль ненадежному коду. Функция обратного вызова злоумышленника выполняется в контексте ВАШЕГО контракта.

Данные: Chainalysis обнаружила, что ~60% высокоценных эксплоитов в 2023-2024 годах были связаны с повторными вызовами или аналогичными паттернами. Ведущие проекты, такие как Yearn, Curve и Balancer, все столкнулись с опасениями по поводу повторных вызовов.

Итог: Используйте Checks-Effects-Interactions по умолчанию. Добавляйте nonReentrant, где это необходимо. Для многоконтрактных систем внедряйте GlobalReentrancyGuard. Потерянные $100M+ могли быть спасены с помощью этих базовых паттернов.

Следите за @TheBlockChainer для более глубокого погружения в безопасность Web3.

ETH-0.75%
CRV9.07%
BAL-2.62%
Посмотреть Оригинал
На этой странице может содержаться сторонний контент, который предоставляется исключительно в информационных целях (не в качестве заявлений/гарантий) и не должен рассматриваться как поддержка взглядов компании Gate или как финансовый или профессиональный совет. Подробности смотрите в разделе «Отказ от ответственности» .
  • Награда
  • комментарий
  • Репост
  • Поделиться
комментарий
0/400
Нет комментариев
  • Закрепить