Шаблон агента очень распространен при разработке смарт-контрактов, особенно при обновлениях и модульных конструкциях. Прокси-контракты обычно используются для отделения логической реализации от внешнего интерфейса контракта, позволяя обновлять или заменять базовую реализацию без изменения интерфейса. Однако если процесс инициализации прокси-контракта не выполняется должным образом, он может стать точкой входа для атак.
Предположим, у нас есть следующее актерское мастерство Шаблон контракта,вimplementation
Переменная указывает на адрес контракта, где фактически выполняется логика.:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Proxy {
address private implementation;
constructor (address _implementation) {
implementation = _implementation;
}
fallback() external payable {
address impl = implementation;
assembly {
let ptr := mload(0x40)
calldatacopy(ptr, 0, calldatasize())
let result := delegatecall(gas(), impl, ptr, calldatasize(), 0, 0)
assembly {
let free := mload(0x40)
mstore(free, ptr)
mstore(0x40, add(free, 0x20))
}
switch result
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
}
Этот прокси-контракт принимает адрес контракта реализации через конструктор и сохраняет его в переменной реализации. После этого любые транзакции, отправленные в прокси-контракт, будут перенаправлены в контракт реализации.
Проблема в том, что если у конструктора нет соответствующих ограничений на то, кто может установить адрес реализации, злоумышленник может воспользоваться этим и напрямую вызвать конструктор прокси-контракта, отправив транзакцию, таким образом изменив адрес реализации, чтобы он указывал на его собственный вредоносный адрес. договор. Таким образом, все последующие вызовы будут перенаправлены на вредоносный контракт, что приведет к нарушению функциональности контракта или краже средств.
Чтобы предотвратить атаки такого типа, нам необходимо убедиться, что актерское Процесс инициализации мастерского контракта безопасен. Вот одно из возможных решений:
решение Пример:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
abstract contract Initializable {
bool initialized = false;
modifier initializer() {
require(!initialized, "Already initialized");
initialized = true;
_;
}
}
contract SecureProxy is Initializable {
address private implementation;
address private admin;
constructor(address _implementation, address _admin) initializer {
implementation = _implementation;
admin = _admin;
}
function setImplementation(address _newImplementation) public {
require(msg.sender == admin, "Only admin can set the implementation");
implementation = _newImplementation;
}
fallback() external payable {
// ... (same as before)
}
}
В этой улучшенной версии мы представили абстрактный контракт Initializable для управления состоянием инициализации и применили модификатор инициализатора к конструктору. Кроме того, мы добавили метод setImplementation, который позволяет владельцу контракта (администратору) обновлять адрес реализации, что еще больше повышает безопасность.