智能合约安全漏洞之区块链的时间戳依赖漏洞与解决方法

漏洞原理

时间戳依赖是指智能合约的执行依赖于当前区块的时间戳,如果时间戳不同,那么合约的执行结果也有差别。智能合约中取得时间戳只能依赖某个节点(矿工)来做到。这就是说,合约中取得的时间戳是由运行其代码的节点(矿工)的计算机本地时间决定的。理论上,这个本地时间是可以由矿工控制的。因此,如果在智能合约中不正确地使用区块时间戳,这将是非常危险的。

实例演示

让我们来看下面这个游戏合约,这个合约很容易被矿工利用。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22;

contract Roulette {
    uint public pastBlockTime;
 
    // initially contract
    constructor() {}

    // receive function
    receive() external payable {}

    // fallback function used to make a bet
    fallback() external payable {
        require(msg.value == 1 ether); // must send 1 ether to play
        require(block.timestamp != pastBlockTime);  // only 1 transaction per block
        pastBlockTime = block.timestamp;
        if(block.timestamp % 15 == 0) { // winner
            payable(msg.sender).transfer(address(this).balance);
        }
    }
}

这个合约就像一个简单的彩票。每个区块的一笔交易可以押注1个以太币,以获得合约余额的机会。根据以上合约的逻辑,玩家中彩票的概率是1/15。

然而,正如我们所知,如果需要,矿工可以调整时间戳。在这种特殊情况下,如果合约中有足够的以太币,那么矿工就会被激励选择这样一个区块的时间戳,让时间戳 block.timestampnow 取15的模等于0。这样,他们可能会赢得这个合约中锁定的以太币区块奖励。由于每个区块只允许一个人下注,这让合约容易受到攻击。

判定条件

合约中改变重要状态或者判定条件部分使用了时间戳,如在 Solidity 中使用 block.timestampnow 获取当前区块的时间戳。

解决方法

有以下几种解决方法可供参考:

  1. 在合约中不使用区块时间戳作为判定胜负或改变重要状态的决定性因素,通过指定区块编号来更改合约状态的方法可能是更安全的,因为矿工无法轻松的操控区块号。
  2. 采用一个链外的第三方服务,比如使用 Oraclize 来获取随机数。因为 Oraclize 是一种公共基础服务,不会针对特定的合约“作假”,所以这可以认为是“相对安全的”。

我们不建议采用第二种方法,原因是该方法由于采用第三方预言机服务来产生随机数,需要花费更多额外的费用,而且增加了发生新的故障点的可能性。

猜你喜欢

转载自blog.csdn.net/zyq55917/article/details/124193282