默认可见性漏洞说明
Solidity中的函数可以被可见性说明符修饰,可见性说明符会定义用户如何调用Solidity函数。可见性决定一个函数是否可以由用户或其他派生契约在外部调用、只允许内部调用或只允许外部调用。Solidity有四个可见性说明符,详情请参阅 Solidity 文档。为允许用户从外部调用函数,函数的可见性默认为 public
。
如果不恰当使用可见性说明符,或是忽略指定函数应该使用的可见性说明符,可能会造成合约的重大漏洞。
漏洞介绍
函数的可见性默认是 public。因此,不指定任何可见性的函数就可以由用户在外部调用。当开发人员错误地忽略应该是私有的功能(或只能在合约本身内调用)的可见性说明符时,就会造成默认可见性漏洞。
攻击演示
合约HashForEther:如果用户的以太坊地址的最后8个十六进制字符为0,将会得到Ether奖励。
HashForEther.sol:
contract HashForEther {
function withdrawWinnings() {
// Winner if the last 8 hex characters of the address are 0.
require(uint32(msg.sender) == 0);
_sendWinnings();
}
function _sendWinnings() {
msg.sender.transfer(this.balance);
}
}
合约正常逻辑:用户调用合约方法 withdrawWinnings()
,如果用户的以太坊地址符合要求(地址十六进制的最后8位是0),将会自动调用 _sendWinnings()
,得到以太坊奖励。
但是由于函数 _sendWinnings()
没有被可见性说明符修饰,默认为public,任何用户都有权限直接调用函数 _sendWinnings()
,这样任何用户都可以不经过任何检查,直接获得奖励,进行默认可见性漏洞攻击。
漏洞预防
建议开发者总是指定合约中所有功能的可见性,即便这些函数的可见性本就有意设计成 public,这是一种很好的做法。最近版本的Solidity将在编译过程中为没有明确设置可见性的函数显示警告,以鼓励这种做法。
预防演示
改进后的HashForEther.sol:
contract HashForEther {
function withdrawWinnings() public {
// Winner if the last 8 hex characters of the address are 0.
require(uint32(msg.sender) == 0);
_sendWinnings();
}
function _sendWinnings() private {
msg.sender.transfer(this.balance);
}
}