Источник: https://github.com/AnaktaCTF/CTF/blob/main — WEB/Smart-contracts_OWASP_TOP10.md
Введение
Смарт-контракты — это самоисполняемые программы, работающие на блокчейне, которые позволяют двум или более сторонам взаимодействовать напрямую без необходимости доверять централизованным посредникам.
Они обеспечивают прозрачность, надежность и автоматизацию различных процессов, таких как переводы средств, создание токенов, управление децентрализованными биржами и другими dApps.
Однако именно неизменяемость и публичная доступность смарт-контрактов делают их привлекательной мишенью для злоумышленников.
Ошибки в коде смарт-контрактов могут привести к потере значительных средств, как это уже неоднократно происходило в известных инцидентах, таких как атака на The DAO или уязвимости в протоколах DeFi.
Поэтому важно разбираться в типичных уязвимостях и способах их предотвращения.
В данной статье мы подробно рассмотрим ключевые уязвимости смарт-контрактов, механизмы их эксплуатации и методы защиты. Основным источником информации является проект OWASP "Smart Contract Top 10",
а также другие современные исследования в области безопасности блокчейнов.
Безопасность смарт-контрактов
Безопасность смарт-контрактов имеет решающее значение для целостности и надежности блокчейн-приложений. По мере распространения цифровых транзакций и децентрализованных приложений (dApps),
обеспечение безопасности смарт-контрактов от уязвимостей и атак становится первоочередной задачей. В динамично развивающемся ландшафте блокчейн-технологий защита смарт-контрактов означает не только сохранение цифровых активов,
но и поддержание доверия пользователей и заинтересованных сторон. Децентрализованная природа блокчейна делает его главной мишенью для кибератак, и любая уязвимость может привести к значительным финансовым и репутационным потерям.
Поэтому понимание и внедрение мер безопасности смарт-контрактов необходимо для разработчиков, бизнеса и инвесторов.
Ключевые аспекты безопасности смарт-контрактов
- Четкость и простота кода: Писать ясный и простой код, чтобы уменьшить вероятность ошибок и уязвимостей. Сложный код может скрывать потенциальные проблемы и усложнять аудит.
- Формальная проверка: Математическое доказательство правильности кода, чтобы убедиться, что он ведет себя как задумано во всех возможных сценариях.
- Аудиты безопасности: Регулярные аудиты безопасности опытными профессионалами помогают выявлять и устранять уязвимости до того, как они будут использованы злоумышленниками.
- Тестирование и симуляция: Широкое тестирование и моделирование смарт-контрактов в различных сценариях помогает выявлять потенциальные проблемы и обеспечивать ожидаемое поведение контракта при разных условиях.
- Обзор сообществом: Open source проекты выигрывают от обзоров сообществом, где разработчики со всего мира могут проверять и предлагать улучшения по коду.
Повышение безопасности смарт-контрактов
Разработчики должны применять несколько лучших практик для повышения безопасности:
- Аудиты кода: Привлечение сторонних аудиторов безопасности для выявления потенциальных уязвимостей до развертывания. Компании, такие как Certik, специализируются на всестороннем обзоре кода.
- Программы поиска ошибок: Предоставление вознаграждений за обнаружение и сообщение об ошибках стимулирует сообщество к улучшению безопасности.
- Механизмы паузы и обновления: Реализация функций для приостановки выполнения контракта и возможности обновлений может смягчить воздействие обнаруженных уязвимостей.
- Ограничение скорости: Ограничение числа транзакций или суммы средств, находящихся под риском в определенный период времени, может снизить потенциальный ущерб от атак.
OWASP Top 10
Сообщество OWASP (Open Worldwide Application Security Project) опубликовало список Smart Contract Top 10, который предоставляет разработчикам в области Web3 и экспертам по информационной безопасности
подробное описание 10 самых распространенных уязвимостей смарт-контрактов.
Смарт-контракты представляют собой программный код, который автоматизирует проверку и выполнение контрактных условий в блокчейне, тем самым исключая необходимость использования посредников.
Они обеспечивают прозрачность, эффективность и надежность цифровых сделок, однако, как и обычные программы, могут содержать уязвимости и создавать риски для информационной безопасности.
В этом аналитическом обзоре мы представим адаптированную русскоязычную версию списка OWASP Smart Contract Top 10 с комментариями, рекомендациями и простыми примерами.
Изменения 2023-2025г.
Уязвимости контроля доступа (Access Control Vulnerabilities SC01:2025)
Уязвимости этого типа возникают в случае неправильной реализации ограничений, касающихся того, кто и как может взаимодействовать с определенными функциями смарт-контракта. К этой категории уязвимостей относится небезопасное управление правами пользователей, а также ошибки в логике проверки прав. Злоумышленник может использовать такую уязвимость для выполнения несанкционированных действий (например, для вызова функций) при проведении дальнейших атак.
Функциональность зарплатного смарт-контракта, предназначенная для обновления адреса кошелька, случайно оказалась доступна любому пользователю, и ваша зарплата досталась злоумышленнику.
Уязвимость контроля доступа — это недостаток безопасности, который позволяет несанкционированным пользователям получить доступ или изменить данные или функции контракта. Эти уязвимости возникают, когда код контракта не обеспечивает адекватных ограничений доступа в зависимости от уровней разрешений пользователя. Контроль доступа в смарт-контрактах может относиться к управлению и критической логике, такой как эмиссия токенов, голосование по предложениям, вывод средств, приостановка и обновление контрактов, а также изменение прав собственности.
Пример (Уязвимый контракт):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Solidity_AccessControl {
mapping(address => uint256) public balances;
// Функция сжигания без контроля доступа
function burn(address account, uint256 amount) public {
_burn(account, amount);
}
}
Воздействие:
Злоумышленники могут получить несанкционированный доступ к критически важным функциям и данным в контракте, что нарушит его целостность и безопасность.
Уязвимости могут привести к краже средств или активов, управляемых контрактом, что вызовет значительные финансовые убытки для пользователей и заинтересованных сторон.
Ремедиация:
Убедитесь, что функции инициализации могут быть вызваны только один раз и исключительно авторизованными сущностями.
Используйте устоявшиеся шаблоны контроля доступа, такие как Ownable или RBAC (контроль доступа на основе ролей) в ваших контрактах для управления разрешениями и обеспечения того, чтобы только авторизованные пользователи могли получить доступ к определенным функциям. Это можно сделать, добавив соответствующие модификаторы контроля доступа, такие как onlyOwner или пользовательские роли для чувствительных функций.
Пример (безопасная версия):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// Импорт контракта Ownable из OpenZeppelin для управления правами собственности
import "@openzeppelin/contracts/access/Ownable.sol";
contract Solidity_AccessControl is Ownable {
mapping(address => uint256) public balances;
// Функция сжигания с правильным контролем доступа, доступная только владельцу контракта
function burn(address account, uint256 amount) public onlyOwner {
_burn(account, amount);
}
}
Рекомендации по устранению:
-
Используйте в смарт-контракте механизмы Ownable или RBAC;
-
Проводите регулярный аудит исходного кода смарт-контракта для поиска уязвимостей контроля доступа.
Известные случаи атак Access Control Vulnerabilities (SC01:2025)
- HospoWise Hack : A Comprehensive Hack Analysis
- LAND NFT Hack : A Comprehensive Hack Analysis
Манипуляция ценовым оракулом (Price Oracle Manipulation SC02:2025)
Манипуляция ценовым оракулом — это критическая уязвимость в смарт-контрактах, которые зависят от внешних источников данных (оракулов) для получения цен или другой информации. В децентрализованных финансах (DeFi) оракулы используются для предоставления смарт-контрактам данных из реального мира, таких как цены активов. Однако, если данные, предоставляемые оракулом, будут манипулированы, это может привести к некорректному поведению контракта. Злоумышленники могут воспользоваться этим, подделав данные оракула, что приведет к серьезным последствиям, таким как несанкционированное снятие средств, получение чрезмерного кредитного плеча или даже опустошение пулов ликвидности. Для предотвращения таких атак необходимы надлежащие меры защиты и механизмы валидации.
Пример (Уязвимый контракт):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IPriceFeed {
function getLatestPrice() external view returns (int);
}
contract PriceOracleManipulation {
address public owner;
IPriceFeed public priceFeed;
constructor(address _priceFeed) {
owner = msg.sender;
priceFeed = IPriceFeed(_priceFeed);
}
function borrow(uint256 amount) public {
int price = priceFeed.getLatestPrice();
require(price > 0, "Price must be positive");
// Уязвимость: Отсутствует проверка или защита от манипуляции ценой
uint256 collateralValue = uint256(price) * amount;
// Логика заимствования основывается на манипулируемой цене
// Если злоумышленник подделает цену через оракул, он сможет взять в долг больше средств, чем положено
}
function repay(uint256 amount) public {
// Логика возврата займа
}
}
Воздействие:
- Злоумышленники могут манипулировать оракулом для завышения цены актива, что позволит им заимствовать больше средств, чем они могли бы при нормальных условиях.
- В случаях, когда манипулированная цена приводит к ложной оценке залога, добросовестные пользователи могут столкнуться с ликвидацией своих позиций из-за некорректной оценки стоимости.
- Если оракул будет скомпрометирован, атакующие могут использовать поддельные данные для опустошения пулов ликвидности контракта или даже сделать контракт неплатежеспособным.
Рекомендации по устранению:
- Агрегируйте данные из нескольких независимых оракулов, чтобы снизить риск манипуляции со стороны одного источника.
- Установите минимальные и максимальные пороговые значения для цен, получаемых от оракула, чтобы предотвратить резкие колебания цен, влияющие на логику контракта.
- Введите временную блокировку между обновлениями цен, чтобы избежать мгновенных изменений, которые могут быть использованы злоумышленниками.
- Используйте криптографические доказательства для обеспечения подлинности данных, получаемых от оракулов, например, требуйте подписи от доверенных сторон.
Пример (Исправленная версия):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IPriceFeed {
function getLatestPrice() external view returns (int);
}
contract PriceOracleManipulation {
address public owner;
IPriceFeed public priceFeed;
int public minPrice = 1000; // Минимально допустимая цена
int public maxPrice = 2000; // Максимально допустимая цена
constructor(address _priceFeed) {
owner = msg.sender;
priceFeed = IPriceFeed(_priceFeed);
}
function borrow(uint256 amount) public {
int price = priceFeed.getLatestPrice();
require(price > 0 && price >= minPrice && price <= maxPrice, "Price manipulation detected");
uint256 collateralValue = uint256(price) * amount;
// Логика заимствования с использованием проверенной цены
}
function repay(uint256 amount) public {
// Логика возврата займа
}
}
Известные случаи атак Price Oracle Manipulation SC02:2025
- Polter Finance Hack Analysis
- BonqDAO Protocol : A Comprehensive Hack Analysis
Логические ошибки (Logic Errors SC03:2025)
Логические ошибки возникают из-за выполнения неправильных/непредусмотренных действий в отношении смарт-контракта. Они могут привести к некорректной обработке транзакций, неправильному распределению токенов и в итоге к финансовому ущербу.
Смарт-контракт, распределяющий дивиденды между акционерами вашей компании, может содержать ошибку, из-за которой расчет доли каждого инвестора для выплаты дивидендов будет произведен неверно.
Примеры логических ошибок:
- Неправильное распределение наград: Ошибки в расчете распределения наград между участниками, что приводит к несправедливому выделению средств.
- Некорректная эмиссия токенов: Неконтролируемая или ошибочная логика выпуска токенов, позволяющая создавать бесконечное или нежелательное количество токенов.
- Дисбаланс в кредитных пулах: Неверное отслеживание депозитов и снятий, вызывающее несоответствие резервов пула.
Пример (Уязвимый контракт):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Solidity_LogicErrors {
mapping(address => uint256) public userBalances;
uint256 public totalLendingPool;
function deposit() public payable {
userBalances[msg.sender] += msg.value;
totalLendingPool += msg.value;
}
function withdraw(uint256 amount) public {
require(userBalances[msg.sender] >= amount, "Insufficient balance");
// Ошибочная логика: Уменьшение баланса пользователя без корректировки общего пула
userBalances[msg.sender] -= amount;
// Обновление общего пула пропущено.
payable(msg.sender).transfer(amount);
}
function mintReward(address to, uint256 rewardAmount) public {
// Ошибка в логике эмиссии: Нет проверки значения награды
userBalances[to] += rewardAmount;
}
}
Воздействие:
- Логические ошибки могут привести к неожиданному поведению смарт-контракта или сделать его полностью нефункциональным. Возможные последствия:
- Потеря средств: Неверное распределение наград или дисбаланс в пулах может привести к утечке средств контракта.
- Чрезмерная эмиссия токенов: Инфляция предложения токенов подрывает доверие и снижает их стоимость.
- Операционные сбои: Контракт может не выполнять свои предусмотренные функции.
- Эти последствия могут вызвать значительные финансовые и операционные убытки для пользователей и участников проекта.
Пример (Исправленная версия):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Solidity_LogicErrors {
mapping(address => uint256) public userBalances;
uint256 public totalLendingPool;
function deposit() public payable {
userBalances[msg.sender] += msg.value;
totalLendingPool += msg.value;
}
function withdraw(uint256 amount) public {
require(userBalances[msg.sender] >= amount, "Insufficient balance");
// Корректное уменьшение баланса пользователя и обновление общего пула
userBalances[msg.sender] -= amount;
totalLendingPool -= amount;
payable(msg.sender).transfer(amount);
}
function mintReward(address to, uint256 rewardAmount) public {
require(rewardAmount > 0, "Reward amount must be positive");
// Защищенная логика эмиссии
userBalances[to] += rewardAmount;
}
}
Рекомендации по устранению:
-
Используйте инструменты автоматизированного тестирования, чтобы охватить как можно большее количество способов использования смарт-контракта;
-
Проводите регулярный аудит исходного кода смарт-контракта для поиска логических ошибок;
-
Документируйте предполагаемое поведение каждой функции и модуля, после чего сравнивайте его с фактической реализацией.
Известные случаи Logic Errors SC03:2025
- Level Finance Hack : A Comprehensive Hack Analysis
- BNO Hack : A Comprehensive Hack Analysis
Отсутствие валидации входных данных (Lack of Input Validation SC04:2025)
Валидация входных данных необходима для того, чтобы смарт-контракт обрабатывал только корректные и ожидаемые данные. Если контракт не проверяет входящие данные, он подвергается рискам, таким как манипуляция логикой, несанкционированный доступ и непредсказуемое поведение. Например, если контракт предполагает, что данные от пользователя всегда валидны и не проверяет их, злоумышленники могут воспользоваться этим доверием и передать вредоносные данные. Отсутствие валидации компрометирует безопасность и надежность смарт-контракта.
Пример (Уязвимый контракт):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Solidity_LackOfInputValidation {
mapping(address => uint256) public balances;
function setBalance(address user, uint256 amount) public {
// Функция позволяет любому установить произвольный баланс для любого пользователя без проверок.
balances[user] = amount;
}
}
Воздействие:
- Атакующие могут манипулировать входными данными для кражи средств, токенов или нанесения другого финансового ущерба.
- Некорректные данные могут повредить состояние переменных контракта, что приведет к ненадежной и небезопасной работе.
- Злоумышленники могут использовать контракт для несанкционированных транзакций и операций, влияя как на пользователей, так и на всю систему.
Рекомендации по устранению:
- Убедитесь, что входные данные соответствуют ожидаемому типу.
- Проверяйте, что данные находятся в допустимых границах.
- Гарантируйте, что только авторизованные лица могут вызывать определенные функции.
- Валидируйте структуру данных, например формат адресов или длину строк.
- Останавливайте выполнение и предоставляйте четкие сообщения об ошибках при некорректных данных.
Пример (Исправленная версия):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract LackOfInputValidation {
mapping(address => uint256) public balances;
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Caller is not authorized");
_;
}
function setBalance(address user, uint256 amount) public onlyOwner {
require(user != address(0), "Invalid address");
balances[user] = amount;
}
}
Известные случаи Lack of Input Validation SC04:2025
- Convergence Finance : A Comprehensive Hack Analysis
- Socket Gateway : A Comprehensive Hack Analysis
Повторный вход (Reentrancy Attacks SC05:2025)
Это уязвимость смарт-контрактов, которая позволяет злоумышленнику многократно вызывать функцию контракта, не дожидаясь завершения предыдущего вызова. Это может привести к хищению активов,
несанкционированному вызову функций смарт-контракта или к изменению его состояния, которые нарушат его штатную работу.
Представьте, что вы приходите в банкомат, чтобы снять зарплату. Вы вставляете карту, вводите ПИН-код, запрашиваете сумму для снятия наличных.
Банкомат списывает запрошенную сумму и выдает вам наличные. Если вы захотите снять дополнительные деньги, то вам нужно будет начать новую транзакцию.
Как будет выглядеть снятие наличных в случае атаки повторного входа? Вы вставляете карту, вводите ПИН-код, запрашиваете сумму для снятия наличных. В момент,
когда банкомат начинает выдавать деньги, вы снова вводите запрос на снятие, не завершив предыдущую операцию. Банкомат не успевает учесть выдачу денег с предыдущей операции и думает,
что должен вам еще. Таким образом, вы сможете снять денег больше, чем у вас есть на счете.
Пример (Уязвимый контракт):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Solidity_Reentrancy {
mapping(address => uint) public balances;
function deposit() external payable {
balances[msg.sender] += msg.value;
}
function withdraw() external {
uint amount = balances[msg.sender];
require(amount > 0, "Insufficient balance");
// Vulnerability: Ether is sent before updating the user's balance, allowing reentrancy.
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
// Update balance after sending Ether
balances[msg.sender] = 0;
}
}
Воздействие:
- Основной и наиболее опасный эффект — это возможность вывести больше средств, чем положено, что может привести к полному опустошению контракта.
- Злоумышленник может инициировать несанкционированные вызовы других функций, что приводит к непредсказуемым последствиям и дополнительным уязвимостям в экосистеме.
Пример (Безопасная версия):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Solidity_Reentrancy {
mapping(address => uint) public balances;
function deposit() external payable {
balances[msg.sender] += msg.value;
}
function withdraw() external {
uint amount = balances[msg.sender];
require(amount > 0, "Insufficient balance");
// Fix: Update the user's balance before sending Ether
balances[msg.sender] = 0;
// Then send Ether
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
}
Рекомендации по устранению:
- Придерживайтесь использования модели Checks-Effects-Interactions (чтобы убедиться, что все внутренние изменения состояния выполнены до передачи потока управления);
- Используйте механизмы защиты типа mutex, обеспечивающие взаимное исключение исполнения важных участков кода;
- Регулярно обновляйте Solidity до актуальных версий.
Известные случаи атак Reentrancy Attacks (SC05:2025):
- Rari Capital : A Comprehensive Hack Analysis
- Orion Protocol : A Comprehensive Hack Analysis
Непроверенные возвращаемые значения внешних вызовов (Unchecked External Calls SC06:2025)
Такие уязвимости возникают, когда смарт-контракт вызывает другой контракт, но не проверяет результат этого вызова (вызванный контракт может завершиться с ошибкой). Это может привести к нарушению логики работы смарт-контракта, ошибкам при проведении транзакций и потере активов.
Представьте, что вы оплачиваете онлайн-подписку на стриминговый сервис. Если сервис отправит ваш платеж, но не проверит его успешное завершение, это может привести к тому, что деньги спишутся с вашего счета, но не поступят на счет получателя.
Пример (Уязвимый контракт):
// SPDX-License-Identifier: MIT
pragma solidity ^0.4.24;
contract Solidity_UncheckedExternalCall {
address public owner;
constructor() public {
owner = msg.sender;
}
function forward(address callee, bytes _data) public {
require(callee.delegatecall(_data));
}
}
Воздействие:
- Неконтролируемые внешние вызовы могут привести к сбоям транзакций, из-за чего запланированные операции не будут выполнены успешно. Это может привести к потере средств, поскольку контракт может продолжить работу под ложным предположением, что перевод прошел успешно. Кроме того, это может привести к некорректному состоянию контракта, делая его уязвимым к дальнейшим атакам и вызывая несоответствия в логике работы.
Рекомендации по устранению:
-
Всегда проверяйте возвращаемые значения функций call, delegatecall и callcode;
-
Используйте в Solidity функции transfer или send вместо call.value()().
Пример (Исправленная версия):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Solidity_UncheckedExternalCall {
address public owner;
constructor() {
owner = msg.sender;
}
function forward(address callee, bytes memory _data) public {
// Ensure that delegatecall succeeds
(bool success, ) = callee.delegatecall(_data);
require(success, "Delegatecall failed"); // Check the return value to handle failure
}
}
Известные случаи Unchecked External Calls SC06:2025**
- Punk Protocol Hack : A Comprehensive Hack Analysis
Атаки с использованием Flash-займов (Flash Loan Attacks SC07:2025)
Атаки с использованием flash-займов (мгновенных займов) эксплуатируют возможность заимствовать крупные суммы без залога в рамках одной транзакции. Эти атаки используют атомарную природу блокчейн-транзакций, где все операции должны завершаться либо полностью успешно, либо полностью сбоем. Объединяя flash-займы с другими уязвимостями, такими как манипуляция оракулом, повторный вход (reentrancy) или ошибки в логике, злоумышленники могут изменить поведение смарт-контракта и вывести средства.
Примеры атак с использованием Flash-займов:
- Манипуляция оракулом: Использование заимствованных средств для искажения данных ценового оракула и последующего запуска ликвидаций с недостаточным обеспечением.
- Осушение пулов ликвидности: Применение flash-займов для вывода ликвидности или эксплуатации недостатков в механизмах AMM (автоматических маркет-мейкеров).
- Арбитражные атаки: Использование flash-займов для манипуляции ликвидностью и получения прибыли на разнице цен между платформами.
Воздействие
- Потеря средств: Злоумышленники могут опустошить резервы протокола или изменить условия обеспечения займов для кражи активов.
- Нарушение рыночной стабильности: Временные манипуляции ценами или ликвидностью, влияющие на пользователей и платформы.
- Ущерб для экосистемы: Потеря доверия к протоколам, снижение числа пользователей и финансовые убытки.
Рекомендации по устранению
- Исключение зависимости от flash-займов в критической логике: Ограничьте выполнение чувствительных функций только в проверенных и предсказуемых условиях.
- Устойчивые к манипуляциям оракулы: Используйте взвешенные по времени средние цены (TWAP) или децентрализованные оракулы.
- Комплексное тестирование: Проводите тесты с моделированием сценариев атак с использованием flash-займов и проверкой крайних случаев.
- Контроль доступа: Ограничьте доступ к критическим функциям для предотвращения несанкционированных или вредоносных операций.
Известные случаи Flash Loan Attacks SC07:2025
- UwUlend Hack: A Comprehensive Hack Analysis
- Doughfina Hack: A Comprehensive Hack Analysis
Целочисленное переполнение/антипереполнение (Integer Overflow and Underflow SC08:2025)
Уязвимости этого типа возникают тогда, когда значение целого числа (int) в смарт-контракте превышает максимально или минимально допустимые пределы.
Это может привести к несанкционированному изменению логики работы контракта, потере активов и другому ущербу.
Недобросовестный автовладелец не хочет продавать старенький автомобиль всего за 15 тысяч рублей только потому, что пробег на шестизначном одометре его ласточки равен 999 999 км.
Он может воспользоваться уязвимостью «Целочисленное переполнение» — проехать еще один километр, получить пробег в 000 000 км (напоминаем, что там не предусмотрена седьмая цифра) и продать ее в разы дороже.
Ethereum Virtual Machine (EVM) определяет типы данных фиксированного размера для целых чисел. Это означает, что диапазон чисел, которые может представлять переменная типа целое число, конечен. Например, «uint8» (беззнаковое целое число на 8 бит; т.е. неотрицательное) может хранить только целые числа, которые находятся в пределах от 0 до 255. Попытка сохранить значение, превышающее 255, в переменную типа «uint8», приведет к переполнению. Точно так же результатом вычитания «1» из «0» будет 255. Это называется недополнением. Когда арифметическая операция превышает или недостаточна для максимального или минимального размера типа, происходит переполнение или недополнение. Для знаковых целых чисел результат будет немного отличаться. Если мы попытаемся вычесть «1» из переменной типа int8, значение которой равно -128, результат будет 127. Это связано с тем, что знаковые типы целых чисел, которые могут представлять отрицательные значения, начинают отсчет заново, когда мы достигаем наибольшего отрицательного значения. Два простых примера такого поведения включают периодические математические функции (например, прибавление 2 к аргументу синуса оставляет значение неизменным) и одометры в автомобилях, которые отслеживают пройденное расстояние (они сбрасываются на 000000 после превышения максимального числа, т.е. 999999).
Важно:- В Solidity версии 0.8.0 и выше компилятор автоматически проверяет переполнения и недополнения в арифметических операциях, откатывая транзакцию в случае переполнения или недополнения. Solidity 0.8.0 также вводит ключевое слово unchecked, которое позволяет разработчикам выполнять арифметические операции без этих автоматических проверок, явно разрешая переполнение без отката. Это может быть полезно для оптимизации использования газа в случаях, когда переполнение не является проблемой или когда поведение обертки требуется, как в предыдущих версиях Solidity.
Пример (Уязвимый контракт):
// SPDX-License-Identifier: MIT
pragma solidity ^0.4.17;
contract Solidity_OverflowUnderflow {
uint8 public balance;
constructor() public {
balance = 255; // Максимальное значение для uint8
}
// Увеличивает баланс на заданное значение
function increment(uint8 value) public {
balance += value; // Уязвимость к переполнению
}
// Уменьшает баланс на заданное значение
function decrement(uint8 value) public {
balance -= value; // Уязвимость к недополнению
}
}
Воздействие:
Злоумышленник может использовать такие уязвимости для искусственного увеличения баланса аккаунта или количества токенов, что может позволить ему вывести больше средств, чем он законно имеет.
Злоумышленник может изменить запланированный поток логики контракта, что приведет к несанкционированным действиям, таким как кража активов или создание чрезмерного количества токенов.
Рекомендации по устранению:
-
Используйте библиотеки (например, SafeMath), которые содержат функции для проведения безопасных арифметических операций;
-
Используйте Solidity версии 0.8.0 и выше, в котором есть встроенная защита от этой уязвимости.
Пример (Исправленная версия):
Копировать
Редактировать
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Solidity_OverflowUnderflow {
uint8 public balance;
constructor() {
balance = 255; // Максимальное значение для uint8
}
// Увеличивает баланс на заданное значение
function increment(uint8 value) public {
balance += value; // Solidity 0.8.x автоматически проверяет переполнение
}
// Уменьшает баланс на заданное значение
function decrement(uint8 value) public {
require(balance >= value, "Обнаружено недополнение");
balance -= value;
}
}
Известные случаи атак Integer Overflow and Underflow (SC08:2025)
- Rari Capital : A Comprehensive Hack Analysis
- Orion Protocol : A Comprehensive Hack Analysis
Небезопасная генерация случайности (Insecure Randomness SC09:2025)
Такие уязвимости возникают при использовании небезопасных источников для генерации случайных чисел, используемых при проведении транзакций. Это позволяет злоумышленнику предсказать результат генерации и манипулировать им при проведении атак.
Ваш начальник написал смарт-контракт лотереи для розыгрыша премии. В качестве источника случайности используется номер текущего блока. Хитрый сотрудник может проанализировать блокчейн и угадать «случайное» число, чтобы повысить свои шансы на выигрыш.
Небезопасные способы генерации случайных чисел в Solidity:
Разработчики часто используют связанные с блоком методы для генерации случайных чисел, такие как:
- block.timestamp: временная метка текущего блока.
- blockhash(uint blockNumber): хэш указанного блока (только для последних 256 блоков).
- block.difficulty: сложность текущего блока.
- block.number: номер текущего блока.
- block.coinbase: адрес майнера текущего блока.
Эти методы небезопасны, поскольку майнеры могут манипулировать ими и влиять на логику контракта.
Пример (Уязвимый контракт):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract Solidity_InsecureRandomness {
constructor() payable {}
function guess(uint256 _guess) public {
uint256 answer = uint256(
keccak256(
abi.encodePacked(block.timestamp, block.difficulty, msg.sender) // Использование небезопасных методов для генерации случайного числа
)
);
if (_guess == answer) {
(bool sent,) = msg.sender.call{value: 1 ether}("");
require(sent, "Failed to send Ether");
}
}
}
Воздействие:
Небезопасная генерация случайных чисел может быть использована злоумышленниками для получения нечестного преимущества в играх, лотереях и других контрактах, зависящих от случайности. Предсказывая или манипулируя якобы случайными результатами, атакующие могут склонить исход в свою пользу. Это приводит к несправедливым выигрышам, финансовым потерям других участников и подрыву доверия к смарт-контракту.
Рекомендации по устранению:
-
Используйте схемы commit-reveal, в которых пользователи отправляют хешированные значения и раскрывают их позже;
-
Используйте внешние оракул-сервисы.
-
Использование схем коммитмента — криптографическая примитивная техника, которая использует подход commit-reveal. Широко применяется в "подбрасывании монеты", доказательствах с нулевым разглашением и безопасных вычислениях, например: RANDAO.
-
Chainlink VRF — это доказуемо честный и проверяемый генератор случайных чисел (RNG), который позволяет смарт-контрактам получать случайные значения без ущерба для безопасности или удобства.
-
Алгоритм Signidice — подходит для PRNG в приложениях между двумя сторонами с использованием криптографических подписей.
-
Хэши блоков Bitcoin — можно использовать оракулы, такие как BTCRelay, которые служат мостом между Ethereum и Bitcoin. Контракты в Ethereum могут запрашивать будущие хэши блоков в блокчейне Bitcoin как источник энтропии. Следует учитывать, что этот метод подвержен манипуляциям со стороны майнеров и должен использоваться с осторожностью.
Пример (Исправленный контракт):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol";
contract Solidity_InsecureRandomness is VRFConsumerBase {
bytes32 internal keyHash;
uint256 internal fee;
uint256 public randomResult;
constructor(address _vrfCoordinator, address _linkToken, bytes32 _keyHash, uint256 _fee)
VRFConsumerBase(_vrfCoordinator, _linkToken)
{
keyHash = _keyHash;
fee = _fee;
}
function requestRandomNumber() public returns (bytes32 requestId) {
require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK");
return requestRandomness(keyHash, fee);
}
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
randomResult = randomness;
}
function guess(uint256 _guess) public {
require(randomResult > 0, "Random number not generated yet");
if (_guess == randomResult) {
(bool sent,) = msg.sender.call{value: 1 ether}("");
require(sent, "Failed to send Ether");
}
}
}
Известные случаи Insecure Randomness SC09:2025
- Roast Football Hack : A Comprehensive Hack Analysis
- FFIST Hack : A Comprehensive Hack Analysis
Отказ в обслуживании (Denial of Service Attacks SC10:2025)
В контексте смарт-контрактов такие атаки происходят, когда злоумышленник вызывает сбой в работе контракта и таким образом делает невозможным его нормальное использование. Также отказ в обслуживании в смарт-контрактах может происходить из-за ошибок в исходном коде или недостатков в логике работы контракта. Злоумышленник может этим воспользоваться, нарушить штатное функционирование контракта и нанести репутационный или финансовый ущерб.
Если в смарт-контракте есть функции, требующие больших вычислительных мощностей (например, циклы для подсчета количества ваших лайков в телеграм-канале
«Резбез», злоумышленник может вызвать их, чтобы израсходовать весь газ и заблокировать контракт.
Пример (Уязвимый контракт):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract Solidity_DOS {
address public king;
uint256 public balance;
function claimThrone() external payable {
require(msg.value > balance, "Need to pay more to become the king");
//If the current king has a malicious fallback function that reverts, it will prevent the new king from claiming the throne, causing a Denial of Service.
(bool sent,) = king.call{value: balance}("");
require(sent, "Failed to send Ether");
balance = msg.value;
king = msg.sender;
}
}
Воздействие
-Успешная атака типа «отказ в обслуживании» (DoS) может сделать смарт-контракт неработоспособным, не позволяя пользователям взаимодействовать с ним должным образом. Это может нарушить работу критически важных операций и сервисов, зависящих от данного контракта.
-Атаки DoS могут привести к финансовым потерям, особенно в децентрализованных приложениях (dApps), где смарт-контракты управляют средствами или активами.
-DoS-атака может подорвать репутацию смарт-контракта и связанной с ним платформы. Пользователи могут потерять доверие к безопасности и надёжности платформы, что приведёт к оттоку пользователей и потере бизнес-возможностей.
Рекомендации по устранению:
-
Используйте функцию call вместо функций send и transfer;
-
Ограничьте количество действий, которые могут быть выполнены в рамках одной транзакции;
-
Внедрите механизм pull-платежей для возврата или вывода активов, который разделяет процесс начисления и вывода средств на две отдельные транзакции.
Пример (Исправленный контракт):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract Solidity_DOS {
address public king;
uint256 public balance;
// Use a safer approach to transfer funds, like transfer, which has a fixed gas stipend.
// This avoids using call and prevents issues with malicious fallback functions.
function claimThrone() external payable {
require(msg.value > balance, "Need to pay more to become the king");
address previousKing = king;
uint256 previousBalance = balance;
// Update the state before transferring Ether to prevent reentrancy issues.
king = msg.sender;
balance = msg.value;
// Use transfer instead of call to ensure the transaction doesn't fail due to a malicious fallback.
payable(previousKing).transfer(previousBalance);
}
}
Выбывшие из Топ 10 уязвимости
Уязвимости, связанные с лимитом газа (Gas Limit Vulnerabilities)
Уязвимости этого типа возникают, когда для выполнения функции требуется больше ресурсов, чем предусмотрено в блоке. Эксплуатация этой уязвимости может привести к неполному выполнению функций смарт-контракта, что приведет к блокировке активов и заморозке состояния контракта.
Смарт-контракт, который управляет выплатой зарплаты, предполагает выполнение серии операций: расчет суммы к выплате, отправка сотруднику расчетного листа и сам перевод средств. Если для выполнения всех этих операций установлен недостаточный лимит газа, то транзакция не сможет успешно завершиться. Например, расчет суммы к выплате и отправка расчетного листа пройдут успешно, но на стадии перевода средств газ закончится и транзакция отменится, оставив сотрудника голодным.
Рекомендации по устранению:
-
Не используйте циклы, которые итерируются по динамическим структурам данных;
-
Используйте код, эффективно расходующий газ, и внедряйте тестовые функции с большим объемом входных данных, чтобы убедиться, что они не превысят лимит газа для блока;
-
Разбивайте сложные вычисления на несколько транзакций.
Фронтраннинг (Front-running Attacks)
Такие атаки происходят, когда злоумышленник, имеющий доступ к информации об ожидающих исполнения транзакциях, размещает ордер, который принесет ему прибыль от взаимодействия с такой транзакцией. Это позволяет злоумышленнику перехватывать и изменять транзакции, что может привести к финансовому ущербу. Проведение таких атак возможно в публичных блокчейн-сетях, где данные о транзакциях хранятся в открытом доступе.
Вы хотите купить билет на концерт любимого артиста в первые часы продаж. Однако спекулянты, предвосхищая высокий спрос, используют ботов и скупают все билеты, пока вы вручную бронируете места и вводите данные своей карты. Теперь придется покупать билеты с рук в три раза дороже.
Рекомендации по устранению:
-
Используйте схемы commit-reveal, которые скрывают фактические детали транзакции до ее обработки;
-
Используйте механизм пакетных аукционов (batch auctions), который менее подвержен фронтраннингу, поскольку не зависит от порядка транзакций;
-
Измените логику работы смарт-контракта таким образом, чтобы транзакции могли приниматься в любом порядке.
Зависимость от временной метки (Timestamp Dependence)
Уязвимость возникает, когда у хакеров есть возможность манипулировать временными метками, которые могут использоваться при реализации важных функций смарт-контрактов
(генерации псевдослучайных чисел, блокировки активов на определенный промежуток времени и других функций, зависящих от времени), и таким образом влиять на логику работы смарт-контракта.
Хитрый сотрудник турфирмы заработался и не успел купить горящий тур в Дубай, который продавался во внутренней системе бронирования со скидкой 50% до конца рабочего дня.
Он может использовать уязвимость «Зависимость от временной метки» и, отмотав системное время назад, все-таки совершить выгодную покупку.
Рекомендации по устранению:
-
Не используйте свойства block.timestamp или now при реализации ключевых функций смарт-контракта;
-
Если нужно отслеживать время, рассмотрите возможность использования свойства block.number.
Заключение
Применение OWASP Smart Contract Top 10 способствует формированию лучших практик в сфере разработки блокчейн-проектов, что, в свою очередь, повышает доверие пользователей и инвесторов к современным технологиям. Благодаря этому открываются новые возможности для использования таких технологий в различных отраслях экономики, что способствует инновациям и цифровой трансформации.
Безопасность смарт-контрактов является основой успешного и надежного блокчейн-технологий. Понимание общих уязвимостей и применение надежных мер безопасности позволяет разработчикам защищать цифровые активы и поддерживать доверие пользователей. Постоянный мониторинг, аудит и участие сообщества через программы поиска ошибок обеспечивают непрерывную безопасность и устойчивость к возникающим угрозам. По мере роста и интеграции блокчейн-технологий в различные сектора, акцент на безопасность смарт-контрактов станет еще более важным. Превентивные меры и приверженность безопасности помогут создать надежную и защищенную экосистему блокчейна, способствующую инновациям и защищающую пользователей и их активы.
Ресурсы
- Owasp Top 10 Smart-Contracts Vulnerabilitys: https://owasp.org/www-project-smart-contract-top-10/
- Аналитический обзор списка OWASP Smart Contract Top 10: https://rezbez.ru/reviews/analiticheskij-obzor-spiska-owasp-smart-contract-top-10
- Безопасность Смарт-Контрактов: Основные Практики и Уязвимости: https://auditfirst.io/ru/blog/smart-contract-security-key-practices-ru
- Безопасность и Уязвимости Умных Контрактов в Технологии Блокчейн: https://dapp.expert/ru/news/ru_understanding-smart-contracts-and-their-vulnerabilities?utm_source=chatgpt.com
- К вопросу о безопасности смарт-контрактов: https://cyberleninka.ru/article/n/k-voprosu-o-bezopasnosti-smart-kontraktov/viewer