Смарт-контракт Безпека: Розуміння та запобігання вразливостям повторного входу

Атаки повторного входу представляють одну з найзначніших загроз безпеки в розробці смарт-контрактів. Цей технічний аналіз пояснює механіку, що стоїть за вразливостями повторного входу, і надає всебічні стратегії захисту для захисту ваших контрактів.

Що таке атака повторного входу?

Рекурсія відбувається, коли функцію в смарт-контракті можна перервати під час її виконання та викликати знову до того, як перше викликане завершиться. Технічними термінами, рекурсія експлуатує контекст виконання смарт-контракту, маніпулюючи потоком управління через зовнішні виклики.

Коли Контракт A взаємодіє з Контрактом B, вразливість виникає через те, що Контракт B може викликати Контракт A, поки виконання Контракту A ще триває. Цей рекурсивний шаблон викликів може бути використаний для маніпуляції станом контракту та виведення коштів.

Механіка атак: Як працює повторний вхід

Розгляньте сценарій з двома контрактами:

  • Контракт A: Вразливий контракт, що утримує 10 ETH
  • Контракт B: Зловмисний контракт з 1 ETH, депонованим в Контракті A

Потік атаки слідує цій схемі:

  1. Контракт B викликає функцію withdraw() у Контракті A
  2. Контракт A перевіряє, що баланс Контракту B більший за 0 (пройдено перевірку)
  3. Контракт A надсилає 1 ETH до Контракту B перед оновленням свого запису балансу
  4. Передача ETH викликає резервну функцію Контракту B
  5. Усередині функції резервного копіювання Контракт B знову викликає функцію виведення Контракту A ()
  6. Оскільки Контракт A ще не оновив баланс Контракту B, перевірка проходить знову
  7. Контракт A відправляє ще один ETH до Контракту B
  8. Цей цикл повторюється, поки Контракт A не буде вичерпано кошти

Критична вразливість полягає в порядку виконання Контракту A: він виконує зовнішній виклик (, відправляючи ETH) перед оновленням свого внутрішнього стану (, встановлюючи баланс на нуль).

Три техніки захисту від повторного входу

1. Захист на рівні функції з модифікатором noReentrant

Модифікатор noReentrant реалізує механізм блокування, який запобігає рекурсивному виклику функції:

солідність // Змінна стану для відстеження повторного входу bool private locked = false;

// Модифікатор для запобігання повторному входу модифікатор noReentrant() { require(!заблоковано, "Дзвінок повторного входу"); заблоковано = true; _; locked = false; }

// Захищена функція функція withdraw() публічна noReentrant { // Логіка функції тут }

Цей підхід блокує спроби повторного входу, підтримуючи змінну стану на рівні контракту, яка запобігає одночасному виконанню захищених функцій.

2. Перевірка-Ефект-Взаємодія Патерн

Цей шаблон реорганізовує код, щоб слідувати конкретній послідовності операцій:

  1. Перевірте: Підтвердіть умови (, наприклад, баланс > 0)
  2. Ефект: Оновлення змінних стану (, наприклад, встановити баланс = 0)
  3. Взаємодія: Виконати зовнішні виклики (, наприклад, перевести ETH)

Порівняння вразливого та захищеного коду:

Уразливий: солідність функція withdraw() зовнішня { uint bal = balances[msg.sender]; require(bal > 0);

// Взаємодія перед Ефектом (vulnerable)
(bool відправлено, ) = msg.sender.call{value: bal}("");
require(sent, "Не вдалося надіслати ефір");

balances[msg.sender] = 0; // Може ніколи не бути досягнуто, якщо станеться повторний виклик

}

Захищено: солідність function withdraw() зовнішній { uint bal = balances[msg.sender]; require(bal > 0);

// Ефект перед взаємодією (secure)
баланси[msg.sender] = 0;

(bool відправлено, ) = msg.sender.call{value: bal}("");
require(sent, "Не вдалося надіслати Ефір");

}

Оновлюючи стан перед зовнішніми взаємодіями, контракт залишається безпечним, навіть якщо зовнішній виклик викликає повторний виклик функції.

3. Захист між контрактами з GlobalReentrancyGuard

Для проектів з кількома взаємодіючими контрактами спільний захист від повторних викликів забезпечує всебічний захист:

солідність // Центральний контракт для захисту від повторних викликів contract GlobalReentrancyGuard { mapping(address = > bool) приватні _status;

function _beforeNonReentrant() internal {
    require(_status[msg.sender] == false, "ReentrancyGuard: повторний дзвінок");
    _status[msg.sender] = істина;
}

функція _afterNonReentrant() внутрішня {
    _status[msg.sender] = false;
}

}

// Використання охоронця в контракті contract ProtectedContract is GlobalReentrancyGuard { функція protectedFunction() зовнішня { _beforeNonReentrant();

    // Логіка захищеної функції тут
    
    _afterNonReentrant();
}

}

Ця техніка запобігає повторному входу між контрактами, підтримуючи глобальний реєстр станів, який відстежує статус виконання в кількох контрактах у вашій екосистемі.

Кращі практики безпеки

Щоб забезпечити всебічний захист від атак повторного входу:

  1. Застосуйте кілька шарів захисту: Поєднайте патерн перевірки-ефекту-взаємодії з захистом від повторних викликів для максимальної безпеки
  2. Проведіть ретельне тестування: Використовуйте спеціалізовані інструменти, такі як Mythril, для виявлення потенційних вразливостей повторного входу.
  3. Дотримуйтесь встановлених схем: Реалізуйте заходи безпеки послідовно у всіх контрактах вашого проєкту
  4. Використовуйте перевірені залежності: Включайте бібліотеки з перевіреними записами безпеки
  5. Спочатку оновіть баланси: Завжди змінюйте стан перед виконанням зовнішніх викликів або переказів

Здійснюючи ці ці механізми захисту, ви можете ефективно захистити свої смарт-контракти від одного з найнебезпечніших векторів атак у безпеці блокчейну.

ETH3.52%
Переглянути оригінал
Ця сторінка може містити контент третіх осіб, який надається виключно в інформаційних цілях (не в якості запевнень/гарантій) і не повинен розглядатися як схвалення його поглядів компанією Gate, а також як фінансова або професійна консультація. Див. Застереження для отримання детальної інформації.
  • Нагородити
  • Прокоментувати
  • Репост
  • Поділіться
Прокоментувати
0/400
Немає коментарів
  • Закріпити