什么是重入攻击重入攻击(ReentrancyAttack)?要如何解决并避免类似的问题?
重入攻击(Reentrancy Attack)是一种智能合约安全漏洞,攻击者利用合约对外部地址调用时的回调函数,在状态变量更新之前反复调用合约,从而重复执行提款或转账操作,导致资金被盗。
function claimToken(address tokenAddress) external {
require(tokenAddress != address(0), "Invalid token address");
uint256 rewardAmount = userRewardAmounts[msg.sender][tokenAddress];
require(rewardAmount > 0, "No reward available");
if (tokenAddress == ethAddress) {
(bool success, ) = msg.sender.call{value: rewardAmount}("");
require(success, "ETH transfer failed");
} else {
IERC20(tokenAddress).safeTransfer(msg.sender, rewardAmount);
}
userRewardAmounts[msg.sender][tokenAddress] = 0;
tokenBalances[tokenAddress] -= rewardAmount;
}
这种顺序会导致以下问题:
call
进行低级别调用,接收者可以通过fallback
或receive
函数触发重入;safeTransfer
(通常是安全的),但如果代币实现了恶意逻辑(如回调),仍可能被利用。如何实现攻击:
假设攻击者的合约Attacker
作为接收者:
claimToken
请求领取 100 ETH 奖励;Attacker
;Attacker
的receive
函数被触发,立即再次调用claimToken
;userRewardAmounts
仍为 100),合约会再次转账 100 ETH;使用 检查 - 效果 - 交互(Checks-Effects-Interactions) 模式,先更新状态再进行资金转移:
function claimToken(address tokenAddress) external {
require(tokenAddress != address(0), "Invalid token address");
// 1. 检查:读取奖励金额
uint256 rewardAmount = userRewardAmounts[msg.sender][tokenAddress];
require(rewardAmount > 0, "No reward available");
// 2. 效果:立即更新状态(重置奖励和减少余额)
userRewardAmounts[msg.sender][tokenAddress] = 0;
tokenBalances[tokenAddress] -= rewardAmount;
// 3. 交互:最后进行资金转移
if (tokenAddress == ethAddress) {
(bool success, ) = msg.sender.call{value: rewardAmount}("");
require(success, "ETH transfer failed");
} else {
IERC20(tokenAddress).safeTransfer(msg.sender, rewardAmount);
}
}
重入攻击是“先转账后记账”导致的经典漏洞,需通过状态优先更新或锁机制彻底防御。
call
、transfer
、send
)可能触发恶意回调。如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!