Перейти к содержанию

Атака повторного входа

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

Обновлено

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

Краткий ответ

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

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

Как это работает

Типичная атака проходит так:

  1. Атакующий входит в функцию изменения состояния и проходит начальные проверки.
  2. Уязвимый контракт вызывает адрес, токен или протокол до завершения учета.
  3. Код атакующего входит в исходный или связанный контракт, пока видно старое состояние.
  4. Вложенный вызов повторяет эффект или меняет общее состояние, после чего исполнение возвращается с нарушенным инвариантом.

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

Защита должна быть многоуровневой:

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

Пример

Рассмотрим хранилище, которое отправляет средства и обнуляет баланс пользователя только после вызова:

function withdraw() external {
    uint256 amount = balances[msg.sender];
    require(amount > 0, "empty balance");

    (bool ok, ) = msg.sender.call{value: amount}("");
    require(ok, "transfer failed");

    balances[msg.sender] = 0;
}

Получатель получает управление, пока balances[msg.sender] еще содержит старую сумму. Его функция приема может снова вызвать withdraw(), пройти ту же проверку и запросить новый перевод. Обновление баланса до вызова закрывает это окно, а защита может отклонить вложенный вход. Но ни одно изменение не доказывает безопасность всей системы: другая точка входа или связанный контракт могут открыть тот же незавершенный инвариант.

Риски

  • Защита охватывает одну функцию, а другая открывает то же состояние.
  • Локальный порядок вызовов правилен, но межконтрактный инвариант остается несогласованным во время callback.
  • Токен, получатель, callback флеш-кредита или адаптер стратегии неожиданно выполняет внешний код.
  • View-функция публикует временную цену или курс, который другой протокол использует в той же транзакции.
  • Обновление, модуль или изменение разметки хранилища обходит или повреждает исходную блокировку.
  • Тесты охватывают рекурсивный вывод, но пропускают межфункциональные, межконтрактные пути и чтение.

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

Распространенные заблуждения

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

Связанные темы

Источники

Навигация

Поиск по вики...