solidity編寫智能合約的安全漏洞問題(一)

回顧 3 個底層調用 call()delegatecall()callcode() 和 3 個轉幣函數 call.value()()send()transfer()

- call()

call() 用於 Solidity 進行外部調用,例如調用外部合約函數 <address>.call(bytes4(keccak("somefunc(params)"), params)),外部調用 call() 返回一個 bool 值來表明外部調用成功與否:

- delegatecall()

除了 delegatecall() 會將外部代碼作直接作用於合約上下文以外,其他與 call() 一致,同樣也是隻能獲取一個 bool 值來表示調用成功或者失敗(發生異常)。

- callcode()

callcode() 其實是 delegatecall() 之前的一個版本,兩者都是將外部代碼加載到當前上下文中進行執行,但是在 msg.sender 和 msg.value 的指向上卻有差異。

例如 Alice 通過 callcode() 調用了 Bob 合約裏同時 delegatecall() 了 Wendy 合約中的函數,這麼說可能有點抽象,看下面的代碼:

如果還是不明白 callcode() 與 delegatecall() 的區別,可以將上述代碼在 remix-ide 裏測試一下,觀察兩種調用方式在 msg.sender 和 msg.value 上的差異。

- call.value()()

在合約中直接發起 TX 的函數之一(相當危險),

- send()

通過 send() 函數發送 Ether 失敗時直接返回 false;這裏需要注意的一點就是,send() 的目標如果是合約賬戶,則會嘗試調用它的 fallbcak() 函數,fallback() 函數中執行失敗,send() 同樣也只會返回 false。但由於只會提供 2300 Gas 給 fallback() 函數,所以可以防重入漏洞(惡意遞歸調用)。

- transfer()

transfer() 也可以發起 Ether 交易,但與 send() 不同的時,transfer() 是一個較爲安全的轉幣操作,當發送失敗時會自動回滾狀態,該函數調用沒有返回值。同樣的,如果 transfer() 的目標是合約賬戶,也會調用合約的 fallback() 函數,並且只會傳遞 2300 Gas 用於 fallback() 函數執行,可以防止重入漏洞(惡意遞歸調用)。

這裏以一個簡單的示例來說明嚴格驗證底層調用返回值的重要性:

function withdraw(uint256 _amount) public {
    require(balances[msg.sender] >= _amount);
    balances[msg.sender] -= _amount;
    etherLeft -= _amount;
    msg.sender.send(_amount);  // 未驗證 send() 返回值,若 msg.sender 爲合約賬戶 fallback() 調用失敗,則 send() 返回 false
}

上面給出的提幣流程中使用 send() 函數進行轉賬,因爲這裏沒有驗證 send() 返回值,如果 msg.sender 爲合約賬戶 fallback() 調用失敗,則 send() 返回 false,最終導致賬戶餘額減少了,錢卻沒有拿到。

 

這裏先提供一些鏈接便於參考學習:

https://paper.seebug.org/661/

https://news.bitcoinworld.com/a/2599?locale=zh_HANT

https://github.com/ConsenSys/smart-contract-best-practices/blob/master/README-zh.md

後續會有更多有關細節方面的介紹......

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章