《我学区块链》—— 十七、以太坊安全之 recursive 递归调用

十七、以太坊安全之 recursive(递归调用)

       先看下面一段代码:

function withdrawBalance() {
    amountToWithdraw = userBalances[msg.sender];
    if (amountToWithdraw > 0) {
        if (!(msg.sender.call.value(amountToWithdraw)())) {throw;}
        userBalances[msg.sender] = 0;
    }
}

       这是一段给用户取款的代码,让用户一次性从你的智能合约里取回存款。例如,你的合约账户共有 1000 个 ether,而某用户存有 10 个 ether。因为该代码存有严重的递归调用漏洞,该用户可轻松地将你账户里的 1000 个 ether 全部提走。

       首先,该段代码使用了addr.call.value()() 来发送 ether,而不是 send(),给 fallback 函数的调用提供了足够多的 gas。你只要将 fallback 函数写成如下的方式便可取走所有的 ether:

function() {
    address addr = 0x6c8f2a135f6ed072de4503bd7c4999a1a17f824b;
    if (COUNT < 100) {
        addr.call("withdrawBalance");
        COUNT++;
    }
}

       在这段 fallback 代码中,当计数器小于 100 时,递归调用 withdrawBalance 函数。在这种情况下,

msg.sender.call.value(amountToWithdraw)()

将被调用100次,从而取走 100 * 10 ether。

       所以在写智能合约时,需要考虑到它可能被递归调用,在这个 case 里,我们可以这样调整代码以防止递归调用而出现的问题:

function withdrawBalance() {
    amountToWithdraw = userBalances[msg.sender];
    userBalances[msg.sender] = 0;
    if (amountToWithdraw > 0) {
        if (!(msg.sender.call.value(amountToWithdraw)())) {
            userBalances[msg.sender] = amountToWithdraw;
            throw;
        }
    }
}

       是不是很眼熟,没错,知名的 DAO 漏洞,就是 fallback + recursive 的一个结合体,由于笔者之前收录的一份漏洞清单中,fallback 及 recursive 也独立的作为两个漏洞条目,因此,这里再针对性的分析一下。

猜你喜欢

转载自blog.csdn.net/xuguangyuansh/article/details/81329641