Фьючерсы
Доступ к сотням фьючерсов
TradFi
Золото
Одна платформа мировых активов
Опционы
Hot
Торги опционами Vanilla в европейском стиле
Единый счет
Увеличьте эффективность вашего капитала
Демо-торговля
Введение в торговлю фьючерсами
Подготовьтесь к торговле фьючерсами
Фьючерсные события
Получайте награды в событиях
Демо-торговля
Используйте виртуальные средства для торговли без риска
Запуск
CandyDrop
Собирайте конфеты, чтобы заработать аирдропы
Launchpool
Быстрый стейкинг, заработайте потенциальные новые токены
HODLer Airdrop
Удерживайте GT и получайте огромные аирдропы бесплатно
Launchpad
Будьте готовы к следующему крупному токен-проекту
Alpha Points
Торгуйте и получайте аирдропы
Фьючерсные баллы
Зарабатывайте баллы и получайте награды аирдропа
Инвестиции
Simple Earn
Зарабатывайте проценты с помощью неиспользуемых токенов
Автоинвест.
Автоинвестиции на регулярной основе.
Бивалютные инвестиции
Доход от волатильности рынка
Мягкий стейкинг
Получайте вознаграждения с помощью гибкого стейкинга
Криптозаймы
0 Fees
Заложите одну криптовалюту, чтобы занять другую
Центр кредитования
Единый центр кредитования
Глубокое понимание атак Reentrancy и эффективные методы защиты
Атака Reentrancy — одна из наиболее серьёзных уязвимостей в смарт-контрактах Ethereum. Давайте разберёмся, как она работает и как её предотвратить.
**Что такое Reentrancy?**
Reentrancy (реентерабельность) возникает, когда контракт вызывает внешний контракт до завершения его собственного выполнения. Злоумышленник может использовать эту уязвимость для повторного входа в функцию и кражи средств.
**Классический пример — уязвимость DAO:**
```solidity
function withdraw(uint amount) public {
require(balances[msg.sender] >= amount);
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
balances[msg.sender] -= amount;
}
```
Здесь баланс обновляется ПОСЛЕ отправки средств. Злоумышленник может перехватить управление и вызвать `withdraw` снова.
**Эффективные методы защиты:**
1. **Паттерн Checks-Effects-Interactions (CEI):**
```solidity
function withdraw(uint amount) public {
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount;
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
}
```
2. **Блокировка Mutex:**
```solidity
bool locked = false;
modifier noReentrancy() {
require(!locked, "Нет повторных вызовов");
locked = true;
_;
locked = false;
}
function withdraw(uint amount) public noReentrancy {
// Код функции
}
```
3. **ReentrancyGuard от OpenZeppelin:**
```solidity
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract Safe is ReentrancyGuard {
function withdraw(uint amount) public nonReentrant {
// Защищённый код
}
}
```
4. **Паттерн Pull вместо Push:**
```solidity
function withdrawBalance() public {
uint balanceToWithdraw = balances[msg.sender];
balances[msg.sender] = 0;
(bool success, ) = msg.sender.call{value: balanceToWithdraw}("");
require(success);
}
```
**Ключевые рекомендации:**
- Всегда обновляйте состояние ДО внешних вызовов
- Используйте проверенные библиотеки защиты
- Проводите аудиты безопасности
- Тестируйте сценарии reentrancy
- Рассмотрите использование pull-механизмов для платежей
Понимание этих принципов критично для разработки безопасных DeFi приложений.
Атака повторного вызова (reentrancy) является одной из самых серьезных угроз безопасности для смарт-контрактов. В этой статье вы узнаете не только о механизме работы reentrancy, но и о комплексных методах защиты вашего проекта.
Для полного понимания начнем с базовых понятий, затем проанализируем исходный код реальных атак и, наконец, рассмотрим три доказанных метода предотвращения: от модификатора nonReentrant() до GlobalReentrancyGuard() и шаблонов проверки-эффектов-взаимодействий.
Что такое атака типа reentrancy?
Представьте два смарт-контракта, взаимодействующих друг с другом. ContractA и ContractB могут вызывать друг друга. Это кажется нормальным, но именно в этом и кроется уязвимость, которую злоумышленник может использовать.
Ключевое понятие reentrancy: смарт-контракт может вызвать обратный вызов другого контракта, пока тот еще выполняется. Это создает бесконечный цикл, если не контролировать его должным образом.
Например, ContractA держит 10 Ether, из которых ContractB уже отправил 1 Ether. Обычно, при запросе на вывод, контракт проверяет баланс (>0), отправляет Ether и устанавливает баланс в 0. Но при reentrancy этот процесс можно повторять несколько раз до обновления баланса.
Механизм атаки reentrancy
Злоумышленник использует два элемента: функцию attack() и fallback().
Функция fallback — особая функция в Solidity, вызываемая автоматически, если:
Теперь пошагово:
Шаг 1: злоумышленник вызывает attack(), внутри которой вызывает вывод средств из ContractA.
Шаг 2: ContractA проверяет баланс ContractB, если он >0, отправляет 1 Ether и активирует fallback.
Шаг 3: В этот момент баланс еще не обновлен — контракт продолжает выполнение функции вывода.
Шаг 4: Вызов fallback() активируется и сразу вызывает повторный вызов функции вывода ContractA.
Шаг 5: ContractA снова проверяет баланс — он еще не обновлен, и отправляет еще 1 Ether, активируя fallback.
Этот цикл продолжается, пока ContractA не истощит все Ether. Поэтому reentrancy так опасна.
Анализ конкретного кода атаки
Рассмотрим пример смарт-контракта EtherStore с двумя функциями:
Функция withdrawAll() сначала проверяет баланс (>0), отправляет Ether, затем устанавливает баланс в 0. Уязвимость в том, что отправка происходит до обновления баланса.
Злоумышленник создает контракт Attack, который:
Процесс атаки:
Три метода защиты от reentrancy
Метод 1: Использование модификатора nonReentrant
Простое решение — добавить модификатор nonReentrant к функциям, которые требуют защиты. Он блокирует повторные вызовы функции, пока она не завершится.
Работает так:
Недостаток: защищает только одну функцию, не предотвращает цепочку атак между разными функциями.
Метод 2: Модель Check-Effect-Interaction
Более продвинутый подход — менять порядок операций:
Пример опасного кода:
Безопасный порядок:
Обновление баланса происходит до отправки Ether, что предотвращает повторные вызовы.
Метод 3: Глобальный Reentrancy Guard (GlobalReentrancyGuard)
Подходит для сложных систем с несколькими контрактами. Создается центральный контракт-замок, который отслеживает состояние reentrancy для всей системы.
Все контракты ссылаются на один и тот же глобальный замок, что предотвращает цепные атаки между контрактами. Например, если ScheduledTransfer вызывает AttackTransfer, глобальный замок блокирует повторные вызовы.
Выбор подходящего метода
Лучше всего комбинировать эти методы в зависимости от конкретных требований проекта.
Итог
Атака reentrancy — не неизбежна, если понять ее механику и правильно применить защитные меры. От простого модификатора до глобального замка — каждый инструмент важен для обеспечения безопасности смарт-контрактов.
Помните: безопасность — не опция, а необходимость. Понимание и правильное внедрение защитных техник поможет создать надежные и безопасные децентрализованные приложения.
Для получения обновлений по безопасности Web3, Solidity, аудиту смарт-контрактов и другим вопросам следите за @TheBlockChainer в Twitter.