1

我是一名使用 Solidity 和区块链技术的新手,我正在阅读一些好的实践来改进我的代码。

我有一个关于我不太了解的代码的问题:

来源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 步所需的次数。我对此的问题是,恶意代理如何调用 misuse this 并多次调用该行代码。我显然在这里遗漏了一些东西。

4

1 回答 1

3

这被称为重入攻击。

这是不安全的,因为只有在处理完提款后用户的余额才设置为 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])。

这可以通过将行的顺序切换为:

function withdrawBalance() public {
    uint amountToWithdraw = userBalances[msg.sender];
    userBalances[msg.sender] = 0;
    require(msg.sender.call.value(amountToWithdraw)());
}

现在,即使攻击者withdrawBalance再次调用,用户的余额已经被设置为 0,并且无法进一步提款。

于 2018-08-07T11:46:19.427 回答