
智能合約里的 transferFrom 是批準(zhǔn)轉(zhuǎn)賬流程里的關(guān)鍵函數(shù),這個由于不如 transfer 那么常用,容易被不小心忽略。這個流程最大的問題是權(quán)限問題??创a說話:
// 批準(zhǔn)轉(zhuǎn)賬上限(批準(zhǔn)目標(biāo)可以代我轉(zhuǎn)賬的上限)
function approve(address _spender, uint256 _value) public returns (bool success) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
// 代我轉(zhuǎn)賬的流程
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
/// same as above
require(_to != 0x0);
require(balances[_from] >= _value);
require(balances[_to] + _value > balances[_to]);
uint previousBalances = balances[_from] + balances[_to];
balances[_from] -= _value;
balances[_to] += _value;
allowed[_from][msg.sender] -= _value;
Transfer(_from, _to, _value);
assert(balances[_from] + balances[_to] == previousBalances);
return true;
}
可以看出,這個流程并沒做 allowed[_from][msg.sender] 和 _value 的判斷,比如函數(shù)開始應(yīng)該判斷:
require(allowed[_from][msg.sender] >= _value);
如果 allowed[_from][msg.sender] 不存在,那么值是 0,判斷缺失,也就等于之前的 approve 函數(shù)形同虛設(shè)。然后,這還出現(xiàn)了個有趣的溢出:
allowed[_from][msg.sender] -= _value;
當(dāng) allowed[_from][msg.sender] 不存在,那么值是 0,減去 _value(大于 0 時),就溢出了(溢出并不會導(dǎo)致中斷回滾)。這就是為什么如果用了 SafeMath 就會沒問題,因為 SafeMath 會拋出錯誤,直接中斷回滾 transferFrom 函數(shù)。
整體這樣看下來,EDU 和 BAI 等合約的 transferFrom 盜幣事件最核心的問題是權(quán)限問題,溢出在這僅僅是個小插曲而已。
本文摘自慢霧區(qū)微信公眾號文章"智能合約 transferFrom 權(quán)限控制不當(dāng)導(dǎo)致的任意盜幣攻擊簡述"