繁体   English   中英

关于再入攻击的智能合约良好做法

[英]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.valueuserBalances[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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM