Як запобігти атакам з порушенням Access Control у Solidity

Автор статті

Зломи контролю доступу відбуваються тоді, коли контракт Solidity дозволяє ненадійному виконавцю досягти стану, що змінює функціональність, яка має бути обмежена — наприклад, переміщення активів, зміна реалізації, призначення ролей або налаштування довірених адрес. Зловмиснику не потрібно ламати криптографію: він експлуатує відсутність, помилкову або надмірно широке перевіркою авторизації, щоб отримати ефективне підвищення привілеїв.

Вплив може поширюватися на весь протокол, оскільки привілейовані функції часто контролюють об’єднані фонди або критичні для управління налаштування. Poly Network втратив $611.0M 10 серпня 2021 року на Ethereum, BSC і Polygon у інциденті, класифікованому DefiLlama як Належний контроль доступу. Тому авторизація — це не просто питання інтерфейсу користувача або адміністративних деталей. Це частина основної безпекової межі контракту.

Що таке зламаний вразливість контролю доступу у Solidity?

Вразливість із зломаним контролем доступу виникає, коли контракт ненадійно перевіряє, чи «msg.sender» має дозвіл для виконання привілейованої дії, або коли його модель авторизації надає більше повноважень, ніж необхідно цій дії. У Solidity ця помилка зазвичай проявляється у зовнішніх функціях, які змінюють чутливий стан, переказують активи, оновлюють код або змінюють довірених контрагентів.

Контракт може бути вразливим навіть при наявності змінної owner. Головне питання — чи кожен шлях виконання з привілеями перевіряє цю повноваження правильно, включно з допоміжними функціями, ініціалізаторами, функціями управління ролями та хуками оновлення.

Типова атака із зломаним контролем доступу має такий сценарій:

  1. Зловмисник знаходить зовнішню функцію з привілейованими наслідками.
  2. У функції немає перевірки виклику, вона перевіряє неправильну адресу або покладається на стан, контрольований зловмисником.
  3. Зловмисник викликає функцію напряму, обходячи будь-які обмеження frontend.
  4. Такий виклик надає роль, змінює довірену адресу або ініціює операцію переміщення активів.
  5. Зловмисник використовує цю нову можливість для виведення коштів або зміни поведінки протоколу.
  6. Контракт приймає транзакцію, оскільки EVM виконує лише перевірки на ланцюгу.

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

Які шаблони у Solidity спричиняють атаки на контроль доступу та підвищення привілеїв?

Поширені проблеми з контролем доступу виникають, коли розробники відкривають налаштування конфігурації, допоміжні адміністративні функції або шляхи ініціалізації, не враховуючи, що кожна публічна функція є API для потенційних зловмисників. Небезпечний шаблон не обмежений лише відсутністю модифікатора onlyOwner: це також включає авторизацію, яка може бути змінена будь-яким виконавцем, неправильно успадкована, непослідовно застосована або надмірно широка для операції.

Типові помилки включають:

  • Публічні сеттери, що дозволяють будь-кому замінити казну, оракул, реалізацію, оператора або бенефіціара.
  • Функція управління ролями без перевірки адміністратора.
  • Ініціалізатори, які можна викликати після розгортання або якими можуть управляти нецільові аккаунти.
  • Функції, що перевіряють tx.origin замість msg.sender.
  • Привілейована внутрішня функція, доступ до якої досягається через незахищену зовнішню обгортку.
  • Одна роль адміністратора, яка може виконувати всі операційні дії, що збільшує зону уразливості.
  • Авторизація на основі адреси, яку задає користувач, а не нерухомої або контрольованої ролі.
  • Функції оновлення, що перевіряють викликача, але не нову реалізацію або шлях оновлення.

Цей дефектний шаблон особливо хитрий:

function setOperator(address newOperator) external {
    operator = newOperator;
}

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

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

Яким чином виглядає атака зломаного контролю доступу у коді Solidity?

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

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract VulnerablePayoutVault {
    address public payoutOperator;

    constructor(address initialOperator) {
        payoutOperator = initialOperator;
    }

    receive() external payable {}

    function setPayoutOperator(address newOperator) external {
        // Уразливість: будь-який виконавець може надати собі повноваження.
        payoutOperator = newOperator;
    }

    function pay(address payable recipient, uint256 amount) external {
        require(msg.sender == payoutOperator, "not operator");
        require(address(this).balance >= amount, "insufficient balance");

        (bool sent, ) = recipient.call{value: amount}("");
        require(sent, "payment failed");
    }
}

Виправлення розділяє адміністрування та операції. Лише власник може призначати або позбавляти ролі, з обмеженим спектром — роль оператора не може адмініструвати ролі або змінювати власника.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract FixedPayoutVault {
    address public owner;
    mapping(address => bool) public payoutOperators;

    modifier onlyOwner() {
        require(msg.sender == owner, "not owner");
        _;
    }

    modifier onlyPayoutOperator() {
        require(payoutOperators[msg.sender], "not payout operator");
        _;
    }

    constructor(address initialOwner, address initialOperator) {
        require(initialOwner != address(0), "zero owner");
        require(initialOperator != address(0), "zero operator");
        owner = initialOwner;
        payoutOperators[initialOperator] = true;
    }

    receive() external payable {}

    function setPayoutOperator(address operator, bool allowed) external onlyOwner {
        require(operator != address(0), "zero operator");
        // ВИРІШЕННЯ: лише власник може змінювати членство у привілейованій ролі.
        payoutOperators[operator] = allowed;
    }

    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0), "zero owner");
        owner = newOwner;
    }

    function pay(address payable recipient, uint256 amount)
        external
        onlyPayoutOperator
    {
        require(address(this).balance >= amount, "insufficient balance");
        (bool sent, ) = recipient.call{value: amount}("");
        require(sent, "payment failed");
    }
}

Якщо у системі є зміни ролей, оновлення дозволів або зняття з казни, — аудит і безпекова оцінка смарт-контрактів, зосереджена на контролі доступу допоможе прослідкувати, чи кожний маршрут до цих можливостей належним чином обмежений.

Як запобігти неправильним атакам з контролю доступу у Solidity?

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

Техніка Що запобігає Вартість у газі або складність Обмеження
onlyOwner модифікатор Неавторизоване адміністрування функцій власника Один читання з пам’яті на виклик Компомомісований власник має всі повноваження
Мапінги ролей або OpenZeppelin AccessControl Неавторизоване використання операційних ролей Читання з пам’яті та логіка управління ролями Поганий дизайн ролей може створити надмірні привілеї
Ролі з мінімальними привілеями Оператор або “keeper” використовує не релевантні адміністративні повноваження Більше функцій і визначень ролей Потрібні чіткі межі операційної діяльності
Таймлоки для критичних змін Негайне зловмисне оновлення або зміна параметрів Відкладений час виконання та додаткові контракти Не запобігає зловмисним діям після затримки, якщо користувачі можуть реагувати
Мультипідписна адміністрація Один ключ змінює привілейований стан односторонньо Координація підписантів і операційні витрати Підписанти та слабкі політики залишають ризик
Одноразова інаціоналізація Неавторизоване або повторне ініціювання Знак стану у пам’яті і логіка ініціалізації Процедури розгортання мають правильне встановлення влади

Використовуйте добре перевірені примітиви власності та ролей, але не розглядайте бібліотеку як повну систему безпеки. Наприклад, OpenZeppelin Ownable забезпечує механізми власності, але команда протоколу все ще має визначити, чи має власність контролювати паузи, оновлення, збирання зборів, заміну оракулів чи екстрене відновлення.

Уникайте tx.origin для авторизації — воно може бути впливовим через проміжні контракти і не визначає безпосереднього виконавця. Використовуйте msg.sender, а потім перевіряйте його відповідно до об’єкта власника, ролі, мультисива або конкретного правила авторизації контракту.

Які реальні випадки демонструють наслідки атак на контроль доступу?

DefiLlama зафіксував 130 інцидентів із смарт-контрактами, класифікованих як Належний контроль доступу, з 2020 по 2026 рік, без урахування порушень бірж і гаманців, з сумарними збитками у $909.7M. Нижче наведено кілька перевірених інцидентів, що показують, як збої у контролі доступу торкаються мостів, DeFi-протоколів, ботів і застосунків на різних блокчейнах.

Протокол Дата Сума Мережа
Poly Network 2021-08-10 $611.0M Ethereum, BSC, Polygon
Mirror 2021-10-08 $90.0M Terra
Wintermute 2022-06-05 $17.6M Optimism
ValueDefi 2021-05-05 $10.0M BSC
Cork V1 2025-05-28 $12.0M Ethereum
SUPERFORTUNE AI 2026-05-27 $15.2M Ethereum
The Sandbox 2026-08-22 $675K Base, BSC, Ethereum
Radix 2026-08-31 $1.2M Radix

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

Як знаходити уразливості контролю доступу перед розгортанням?

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

Використовуйте цей чек-лист:

  1. Складіть список кожної функції, що переказує активи, змінює ролі, оновлює код, ставить на паузу або змінює довірені адреси.
  2. Визначте передбачуваного виконавця та правило авторизації для кожної функції.
  3. Простежте кожну функцію, що може надавати, відкликати, трансферами або ініціювати цю владу.
  4. Перевірте всі зовнішні обгортки, що мають доступ до привілейованих внутрішніх функцій.
  5. Упевніться, що ініціалізація може відбутися лише один раз і тільки в процесі належного розгортання.
  6. Перевірте, що екстрені ролі не використовуються для непередбачених рутинних або управлінських дій.
  7. Протестуйте відкликання ролей, передачу власності і обробку нульових адрес.
  8. Огляньте проксі, успадковані контракти і шляхи delegatecall у межах тієї ж межі авторизації.

Slither може позначити відсутність патернів авторизації, небезпечне використання tx.origin і неконсистентні модифікатори, але результати потрібно підтверджувати вручну. Fuzzing через Foundry корисний для генерації викликів з будь-яких адрес і для перевірки, що неавторизовані виконавці не можуть змінити привілейований стан.

Значущими для безпеки є інваріанти: наприклад, у інкапсулюваних сценаріях сейфа можна перевірити, що адреса без ролі payout ніколи не зменшить баланс через pay. Інший інваріант може вимагати, щоб лише власник міг змінювати членство ролей. Ці властивості тестують безпекові правила безпосередньо, а не лише очікувані сценарії користувачів.

Безпекова ідея, методологія Soken: переглядайте авторизацію як граф можливостей, а не просто список модифікаторів. Кожен привілейований результат має мати свідомий і аудитуємий шлях до отримання влади, а кожен непередбачений — потенційний шлях підвищення привілеїв.

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

Починайте із створення карти влади для ваших розгорнутих і планових контрактів, включно з проксі і операційними аккаунтами; потім перетворіть кожне правило у модифікатор та інваріанти, що тестують їх. Для глибшого дослідження безпеки Solidity і суміжних класів вразливостей — explore Soken Hub.

Автор статті

Часті запитання

Що таке вразливість з порушенням Access Control у Solidity?

Вразливість з порушенням Access Control у Solidity виникає, коли контракт не підтверджує надійно, що msg.sender має відповідні дозволи для виконавчих дій. Це може дозволити неперевіреному виклику отримати привілеї, змінюючи стан контракту.

Як атаки з порушенням Access Control ведуть до привілеєвого escalations?

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

Які дії у Solidity потрібно захищати за допомогою Access Control?

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

Що трапилося з Poly Network у контексті Access Control?

Poly Network втратив $611.0M 10 серпня 2021 року під час злому, яке було класифіковане як порушення Proper Access Control. Це підкреслює важливість перевірок авторизації для захисту коштів і налаштувань із високим рівнем привілеїв.

Чому авторизація є ключовим бар’єром у безпеці Solidity?

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

Чат