[英]Smart contract good practice for reentrancy attacks
我是一个使用固态和区块链技术的新手,我正在阅读一些改进代码的良好实践。
我有一个关于我不太了解的代码的问题:
来源 : https : //github.com/ConsenSys/smart-contract-best-practices/blob/master/docs/known_attacks.md
// INSECURE
mapping (address => uint) private userBalances;
function withdrawBalance() public {
uint amountToWithdraw = userBalances[msg.sender];
require(msg.sender.call.value(amountToWithdraw)()); // At this point, the caller's code is executed, and can call withdrawBalance again
userBalances[msg.sender] = 0;
}
上面的代码被称为不安全的,因为恶意代理可以调用步骤2所需的次数。 关于这个问题,我的问题是,恶意代理如何调用该滥用代码并多次调用该代码行。 我显然在这里错过了一些东西。
这称为再入攻击。
这是不安全的,因为仅在处理提款后,用户的余额才设置为0。 而且,提款是使用evm的CALL操作码处理的,它将控制权传递给接收地址。
如果接收地址是合同,则可以使用后备功能劫持此转移。 在此后备功能内,它可以检查发送合同的余额是否超过已转移的金额。 如果是这样,它将再次调用withdrawBalance
,直到提款合约的余额用完为止。
一个简单的攻击者合同可能看起来像:
contract Attacker {
function startattack() {
victim.withdrawBalance();
}
function() payable {
if (victim.balance > msg.value) {
victim.withdrawBalance();
}
}
}
通过调用startattack
,您可以开始取款。 当require(msg.sender.call.value(amountToWithdraw)());
行执行后,它将运行后备功能中的代码。 此时, msg.value
是userBalances[msg.sender]
。 攻击者可以检查受害人的合同是否还有比userBalances[msg.sender]
可用的userBalances[msg.sender]
,然后再次运行提款(这将导致该过程循环进行,直到余额下降到userBalances[msg.sender]
)。
可以通过将行的顺序切换为以下方式来避免这种情况:
function withdrawBalance() public {
uint amountToWithdraw = userBalances[msg.sender];
userBalances[msg.sender] = 0;
require(msg.sender.call.value(amountToWithdraw)());
}
现在,即使攻击者再次调用withdrawBalance
,用户的余额已被设置为0,并且将无法进一步取款。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.