Быстрая версия: Рекурсивный вызов похож на хакера, который перезванивает вам, пока вы все еще переводите ему деньги — они опустошают ваш кошелек, прежде чем транзакция завершится.
Вот жестокая правда: Более $100M было потеряно из-за эксплойтов повторного входа. Самый известный? Взлом DAO (2016) украл $50M в ETH, используя именно эту уязвимость.
Как работает атака (Используя реальную логику кода)
Представьте, что ContractA владеет 10 ETH, а ContractB имеет баланс в 1 ETH, хранящийся внутри него.
Когда ContractB вызывает withdrawAll(), вот что должно произойти:
Проверьте баланс > 0 ✓
Отправить ETH обратно ✓
Обновить баланс на 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;
}
Эффекты: Обновить состояние (баланс = 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.
На этой странице может содержаться сторонний контент, который предоставляется исключительно в информационных целях (не в качестве заявлений/гарантий) и не должен рассматриваться как поддержка взглядов компании Gate или как финансовый или профессиональный совет. Подробности смотрите в разделе «Отказ от ответственности» .
Атака повторного входа: почему Смарт-контракты продолжают истощаться ( и как это остановить )
Быстрая версия: Рекурсивный вызов похож на хакера, который перезванивает вам, пока вы все еще переводите ему деньги — они опустошают ваш кошелек, прежде чем транзакция завершится.
Вот жестокая правда: Более $100M было потеряно из-за эксплойтов повторного входа. Самый известный? Взлом DAO (2016) украл $50M в ETH, используя именно эту уязвимость.
Как работает атака (Используя реальную логику кода)
Представьте, что ContractA владеет 10 ETH, а ContractB имеет баланс в 1 ETH, хранящийся внутри него.
Когда ContractB вызывает withdrawAll(), вот что должно произойти:
Но вот где это ломается: нападающий использует порядок операций.
Поток эксплуатации:
Ключевое понимание: Обновление баланса происходит ПОСЛЕ перевода ETH. Это окно уязвимости.
Три стратегии обороны
1. Модификатор nonReentrant (Защита единственной функции)
Заблокируйте функцию во время её выполнения. Повторный вход не допускается: солидность модификатор nonReentrant { require(!locked, “Нет повторного входа”); заблокировано = true; _; locked = false; }
Просто, но защищает только одну функцию за раз.
2. Шаблон Проверок-Эффектов-Взаимодействий (Многофункциональная защита)
Это меняет правила игры:
Неверный заказ:
требуется(баланс > 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.