SWC智能合约漏洞库

在线工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器

SWC-116/使用区块值作为时间计量

合同通常需要访问时间值以执行某些类型的功能。block.timestamp和block.number这样的值 可以使你了解当前时间或时间增量,但是,在大多数情况下使用它们并不安全。

对于block.timestamp,开发人员经常尝试使用它来触发时间相关的事件。由于以太坊是去中心化的 节点只能在某种程度上同步时间。此外,恶意矿工可以更改其区块的时间戳,特别是如果他们可以 通过这样做获益的话。但是,矿工不能将时间戳设置为比前一个时间戳小(否则该区块将被拒绝), 也不能将时间戳设置得太远。考虑到以上所有因素,开发人员无法依赖所提供时间戳的准确性。

至于block.number,以太坊的区块时间通常约为14秒,因此可以预测区块之间的时间增量。但是, 区块时间不是恒定的,并且由于各种原因(例如,分叉重组和难度炸弹)可能会发生变化。由于 块时间可变,因此也不应该依赖于block.number进行精确的时间计算。

CWE漏洞分类

CWE-829:包含来不受信域的功能

整改方案

开发人员应在编写智能合约时注意区块值并不精确,使用它们会导致意想不到的结果。作为替代方案, 在需要使用精确时间时,可以使用预言机/Oracle。

参考文献

合约示例

time_lock.sol

/*
 * @author: Kaden Zipfel
 */

pragma solidity ^0.5.0;

contract TimeLock {
    struct User {
        uint amount; // amount locked (in eth)
        uint unlockBlock; // minimum block to unlock eth
    }

    mapping(address => User) private users;

    // Tokens should be locked for exact time specified
    function lockEth(uint _time, uint _amount) public payable {
        require(msg.value == _amount, 'must send exact amount');
        users[msg.sender].unlockBlock = block.number + (_time / 14);
        users[msg.sender].amount = _amount;
    }

    // Withdraw tokens if lock period is over
    function withdraw() public {
        require(users[msg.sender].amount > 0, 'no amount locked');
        require(block.number >= users[msg.sender].unlockBlock, 'lock period not over');

        uint amount = users[msg.sender].amount;
        users[msg.sender].amount = 0;
        (bool success, ) = msg.sender.call.value(amount)("");
        require(success, 'transfer failed');
    }
}

time_lock.yaml

description: Time lock contract using block number as proxy for time
issues:
- id: SWC-116
  count: 1
  locations: 
  - bytecode_offsets: {}
    line_numbers:
      time_lock.sol: [18]

timed_crowdsale.sol

pragma solidity ^0.5.0;

contract TimedCrowdsale {

  event Finished();
  event notFinished();

  // Sale should finish exactly at January 1, 2019
  function isSaleFinished() private returns (bool) {
    return block.timestamp >= 1546300800;
  }

  function run() public {
    if (isSaleFinished()) {
        emit Finished();
    } else {
        emit notFinished();
    }
  }

}

timed_crowdsale.yaml

description: Timestamp Dependence
issues:
- id: SWC-116
  count: 1
  locations:
  - bytecode_offsets:
      '0x68b8b78a805010c7bf2cd5f199e998845c732f1b8265677d4069efd67dca84d8': [63]
    line_numbers:
      timed_crowdsale.sol: [14]