长篇干货|以太坊智能合约 —— ***安全开发指南(附代码) (7)

另一个经常被提及的解决办法是(译者注:像传统多线程编程中一样)使用mutex。它会"lock" 当前状态,只有锁的当前拥有者能够更改当前状态。一个简单的例子如下:

// Note: This is a rudimentary example, and mutexes are particularly useful where there is substantial logic and/or shared state mapping (address => uint) private balances;

bool private lockBalances;

function deposit() payable public returns (bool) {

if (!lockBalances) {        lockBalances = true;        balances[msg.sender] += msg.value;        lockBalances = false;        return true;    }

throw;

}

function withdraw(uint amount) payable public returns (bool) {

if (!lockBalances && amount > 0 && balances[msg.sender] >= amount) {        lockBalances = true;

if (msg.sender.call(amount)()) { // Normally insecure, but the mutex saves it          balances[msg.sender] -= amount;        }

lockBalances = false;        return true;    }

throw;

}

如果用户试图在第一次调用结束前第二次调用 withdraw(),将会被锁住。 这看上去很有效果,但当你使用多个合约互相交互时问题变得严峻了。 下面是一段不安全的代码:

// INSECURE contract StateHolder {    uint private n;    address private lockHolder;

function getLock() {        if (lockHolder != 0) { throw; }        lockHolder = msg.sender;    }

function releaseLock() {        lockHolder = 0;    }

function set(uint newState) {        if (msg.sender != lockHolder) { throw; }        n = newState;    } }

攻击者可以只调用getLock(),然后就不再调用 releaseLock()。如果他们真这样做,那么这个合约将会被永久锁住,任何接下来的操作都不会发生了。如果你使用mutexs来避免竞态,那么一定要确保没有地方能够打断锁的进程或绝不释放锁。(这里还有一个潜在的威胁,比如死锁和活锁。在你决定使用锁之前***大量阅读相关文献*(译者注:这是真的,传统的在多线程环境下对锁的使用一直是个容易犯错的地方))*

有些人可能会发反对使用该术语 竞态,因为以太坊并没有真正意思上实现并行执行。然而在逻辑上依然存在对资源的竞争,同样的陷阱和潜在的解决方案。

交易顺序依赖(TOD) / 前面的先运行

以上是涉及攻击者在单个交易内执行恶意代码产生竞态的示例。接下来演示在区块链本身运作原理导致的竞态:(同一个block内的)交易顺序很容易受到操纵。

由于交易在短暂的时间内会先存放到mempool中,所以在矿工将其打包进block之前,是可以知道会发生什么动作的。这对于一个去中心化的市场来说是麻烦的,因为可以查看到代币的交易信息,并且可以在它被打包进block之前改变交易顺序。避免这一点很困难,因为它归结为具体的合同本身。例如,在市场上,***实施批量拍卖(这也可以防止高频交易问题)。 另一种使用预提交方案的方法(“我稍后会提供详细信息”)。

时间戳依赖

请注意,块的时间戳可以由矿工操纵,并且应考虑时间戳的所有直接和间接使用。 区块数量平均出块时间可用于估计时间,但这不是区块时间在未来可能改变(例如Casper期望的更改)的证明。

uint someVariable = now + 1;

if (now % 2 == 0) { // the now can be manipulated by the miner

}

if ((someVariable - 100) % 2 == 0) { // someVariable can be manipulated by the miner

}

整数上溢和下溢

这里大概有 20关于上溢和下溢的例子。

(https://github.com/ethereum/solidity/issues/796#issuecomment-253578925)

考虑如下这个简单的转账操作:

mapping (address => uint256) public balanceOf;

// INSECURE

function transfer(address _to, uint256 _value) {    /* Check if sender has balance */

if (balanceOf[msg.sender] < _value)        throw;    /* Add and subtract new balances */    balanceOf[msg.sender] -= _value;    balanceOf[_to] += _value;

}

// SECURE

function transfer(address _to, uint256 _value) {    /* Check if sender has balance and for overflows */     if (balanceOf[msg.sender] < _value || balanceOf[_to] + _value < balanceOf[_to])        throw;

/* Add and subtract new balances */    balanceOf[msg.sender] -= _value;    balanceOf[_to] += _value;

}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgxdjj.html