《我学区块链》—— 三十一、以太坊安全之 可重入漏洞

三十一、以太坊安全之 可重入漏洞

如果一个函数在执行完成前被调用了数次,发生意料不到的行为时,可重入漏洞就可能出现。

我们看看下面这个函数,它可以用于从合约中提取调用者的总余额:

mapping (address => uint) private balances;
function payOut () {
    require(msg.sender.call.value(balances[msg.sender])()); 
    balances[msg.sender] = 0;
}

调用 call.value() 会导致合约外部代码的执行。即,若调用者是另一份合约,这就意味着合约回退措施的执行。这可能会在余额调整为 0 之前再次调用 payOut(),从而获得比可用资金更多的资金。

这种情况下的解决方法就是使用替代函数 send() 或 transfer(),后两者函数能为基础运作提供足够的 Gas,而想要再次调用 payOut() 时 Gas 就不足了。

注意,下面这句话也十分重要:

“若合约含有两个共享状态的函数,那么不需要重复调用函数也可能会发生相似的竞态条件(Race Conditions)。”

因此,最好的做法是在转账前更改状态,即转移资金前,在上述代码中把余额设为 0。

The DAO 攻击利用了该漏洞的一种变体。

猜你喜欢

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