При разработке смарт-контрактов вызов ненадежных внешних контрактов является распространенной угрозой безопасности. Это связано с тем, что когда вы вызываете функцию другого контракта, вы фактически выполняете код этого контракта, что может привести к неожиданному поведению, включая вредоносное поведение. Ниже я приведу пример, чтобы проиллюстрировать этот риск и предложить соответствующие стратегии его смягчения.
Предположим, у нас есть смарт-контракт, который позволяет пользователям выполнять определенную задачу, например погашение токенов, путем вызова внешнего контракта. Здесь мы предполагаем, что внешний контракт предоставляет функцию TransferFrom для перевода токенов с одного аккаунта на другой.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ExternalCallVulnerable {
address public externalTokenContract;
constructor(address _externalTokenContract) {
externalTokenContract = _externalTokenContract;
}
function exchangeTokens(uint256 amount) public {
IERC20(externalTokenContract).transferFrom(msg.sender, address(this), amount);
}
}
В этом контракте функция ExchangeTokens вызывает функцию TransferFrom внешнего контракта. Однако здесь есть потенциальная проблема: внешний контракт может содержать вредоносный код, либо его логика может не соответствовать ожиданиям, что приведет к потере средств или другим нежелательным последствиям.
Злоумышленник может развернуть вредоносный контракт токена ERC20 и передать этот адрес контракта нашему контракту. Вредоносный контракт может содержать дополнительную логику в функции TransferFrom, например, вызов других функций нашего контракта при передаче токенов или выполнение каких-либо несанкционированных операций.
// Пример вредоносного контракта
contract MaliciousToken is IERC20 {
function transferFrom(address, address, uint256) public override returns (bool) {
// Обычная логика передачи токена...
// Выполнять дополнительные вредоносные действия, такие как вызов другой функции в контракте.
ExternalCallVulnerable(0x...).someUnsafeFunction();
return true;
}
}
Когда пользователь пытается обменять токены во вредоносном контракте через наш контракт, будет вызвана функция TransferFrom вредоносного контракта для выполнения вредоносных операций.
Чтобы снизить риски, вызванные внешними вызовами, мы можем принять следующие меры:
Вот пример улучшенного контракта, реализующего механизм белого списка:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC20 {
function transferFrom(address, address, uint256) external returns (bool);
}
contract SafeExternalCall {
mapping(address => bool) public approvedContracts;
address public externalTokenContract;
constructor(address _externalTokenContract) {
approveContract(_externalTokenContract);
externalTokenContract = _externalTokenContract;
}
function exchangeTokens(uint256 amount) public {
require(approvedContracts[externalTokenContract], "Contract not approved");
IERC20(externalTokenContract).transferFrom(msg.sender, address(this), amount);
}
function approveContract(address contractAddress) public {
approvedContracts[contractAddress] = true;
}
}
При разработке смарт-контрактов вызов ненадежных внешних контрактов является распространенной угрозой безопасности. Это связано с тем, что когда вы вызываете функцию другого контракта, вы фактически выполняете код этого контракта, что может привести к неожиданному поведению, включая вредоносное поведение. Ниже я приведу пример, чтобы проиллюстрировать этот риск и предложить соответствующие стратегии его смягчения.
Предположим, у нас есть смарт-контракт,Он позволяет пользователям выполнять определенные задачи, вызывая внешний контракт.,Например, обмен токенов. здесь,Мы предполагаем, что внешний контракт обеспечиваетtransferFrom
функция,Используется для перевода токенов с одного аккаунта на другой.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ExternalCallVulnerable {
address public externalTokenContract;
constructor(address _externalTokenContract) {
externalTokenContract = _externalTokenContract;
}
function exchangeTokens(uint256 amount) public {
IERC20(externalTokenContract).transferFrom(msg.sender, address(this), amount);
}
}
в этом контракте,exchangeTokens
функция Называется внешний контрактtransferFrom
функция。Однако,Здесь есть потенциальная проблема: внешние контракты могут содержать вредоносный код.,Или логика может не соответствовать ожиданиям,приводящие к потере средств или другим неблагоприятным последствиям.
Злоумышленник может развернуть вредоносный контракт токена ERC20.,и передать этот адрес контракта в наш контракт。Вредоносные контракты могут бытьtransferFrom
функция Содержит дополнительную логику в,Например, при передаче токенов,Вызов другой функции в нашем контракте,Или выполнить какие-то несанкционированные операции.
// Пример вредоносного контракта
contract MaliciousToken is IERC20 {
function transferFrom(address, address, uint256) public override returns (bool) {
// Обычная логика передачи токена...
// Выполнять дополнительные вредоносные действия, такие как вызов другой функции в контракте.
ExternalCallVulnerable(0x...).someUnsafeFunction();
return true;
}
}
Когда пользователь пытается обменять токены из вредоносного контракта через наш контракт,вредоносный контрактtransferFrom
функциябудет вызван,Выполнять вредоносные действия.
Чтобы снизить риски, вызванные внешними вызовами, мы можем принять следующие меры:
Вот пример улучшенного контракта, реализующего механизм белого списка:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC20 {
function transferFrom(address, address, uint256) external returns (bool);
}
contract SafeExternalCall {
mapping(address => bool) public approvedContracts;
address public externalTokenContract;
constructor(address _externalTokenContract) {
approveContract(_externalTokenContract);
externalTokenContract = _externalTokenContract;
}
function exchangeTokens(uint256 amount) public {
require(approvedContracts[externalTokenContract], "Contract not approved");
IERC20(externalTokenContract).transferFrom(msg.sender, address(this), amount);
}
function approveContract(address contractAddress) public {
approvedContracts[contractAddress] = true;
}
}
В этом улучшенном контракте,Мы добавилиapprovedContracts
картографирование,Используется для хранения утвержденных адресов внешних контрактов. Только если адрес внешнего контракта внесен в белый список,Его можно вызвать через наш договор.
Благодаря этим улучшениям мы можем значительно снизить риски безопасности, связанные с вызовом ненадежных внешних контрактов. Однако в практических приложениях необходимо постоянно обращать внимание на новые угрозы безопасности и лучшие практики для поддержания безопасности контракта.