• <option id="cacee"><noscript id="cacee"></noscript></option>
  • <table id="cacee"><noscript id="cacee"></noscript></table>
  • <td id="cacee"></td>
  • <option id="cacee"></option>
  • <table id="cacee"></table>
  • <option id="cacee"><option id="cacee"></option></option>
  • <table id="cacee"><source id="cacee"></source></table><td id="cacee"><rt id="cacee"></rt></td>
    <option id="cacee"><option id="cacee"></option></option>
     找回密碼
     立即注冊

    掃一掃,登錄網站

    首頁 自媒體 查看內容
    • 2503
    • 0
    • 分享到

    編寫智能合約的注意事項

    2018-9-25 11:22

    來源: blockchainbrother

    轟動一時的BEC事件在幣圈和鏈圈掀起了不小的反響,智能合約安全一度成為焦點話題。通過一個小小的整數溢出漏洞,黑客盜取了巨量的BEC token,迫使交易所不得不暫時停止交易。一時間BEC價格呈現斷崖式下跌。隨后,SMT token 的智能合約也因為相同的問題遭到了攻擊。兩次事件都給項目發行方和token持有者造成了巨額的損失。


    公開數據表明,大量已經部署的智能合約或多或少都存在著一定的安全風險和漏洞,BEC事件也只是冰山一角。那么就ERC20合約而言,除了整數溢出漏洞以外,還有可能面臨哪些風險呢?

    可重入


    若一個程序或子程序可以「在任意時刻被中斷然后操作系統調度執行另外一段代碼,這段代碼又調用了該子程序不會出錯」,則稱其為可重入(reentrant或re-entrancy)的。


    在智能合約代碼中,黑客可以利用fallback函數,來遞歸調用包含 call.value() 的函數,從合約中重復提取以太幣。通常造成該類事件的原因是余額校驗不到位或余額更新不及時。


    當ERC20代幣合約中涉及以太幣的轉出,應當尤為注意。以一個 withdraw 函數為例,這也是ERC20合約中經常出現的函數。以下代碼中首先進行余額校驗,并向msg.sender地址轉出以太幣,再修改 balance 數組中余額值。


    當函數執行到 msg.sender.call.value(amount)() 時,黑客就可以通過msg.sender的 fallback函數來重復調用 withdraw,進而重復執行 msg.sender.call.value(_amount)(),直到gas全部消耗完畢或者合約中的以太余額全部被取完。于是就給黑客實現 reentrancy 攻擊創造了條件。



    因此,SECBIT實驗室的工程師強烈建議智能合約開發者在轉賬之前做好余額校驗工作,并且將余額計算放在轉賬之前處理。



    另外,值得重視的是,除了 call.value() 以外,任何直接或者間接調用call方法的步驟,都有可能引起回調,從而引發重入的安全事件。


    轉賬方式風險


    當然,杜絕上述問題的一個更好的方式就是不使用 call.value() 進行轉賬。發起以太幣轉賬的方式有三種transfer() ,send() 和 call.value()。我們對這三種方式進行比較。



    如上圖所示,其中最安全的方式當屬 transfer(),一旦轉賬失敗,transfer() 會拋出異常直接觸發 revert() 事件,而另外兩者不會,需要開發人員手動處理返回值。send() 與 transfer() 唯一的區別也在于返回值,通常我們可以認為addr.transfer(v) 就相當于require(addr.send(v))。


    而call.value() 與另外兩者一個最明顯的區別在于gas的限制上面,call.value()允許消耗掉所有的gas。但另外兩種方式由于gas消耗限制到2300,不足以完成遞歸調用,這也是能夠避免 reentrancy 攻擊的原因,上述 withdraw 的代碼可以按照以下的寫法來實現。



    所以,SECBIT實驗室的工程師建議開發人員,在ERC20合約開發過程中,


    如果遇到以太幣轉出的情況,如非必要,盡量選擇使用transfer()函數來完成。


    避免混淆 tx.origin 和 msg.sender


    solidity提供了兩種標準的方式來獲取合約調用方的地址,tx.origin 和 msg.sender,但是這兩者的含義是不同的,其中 msg.sender 是指直接調用當前合約的調用方地址,tx.origin 是指發起本次調用的起始調用方地址。


    比如合約(或外賬戶地址)A去調用合約B,合約B調用合約C。此時在合約C中讀取到的 msg.sender 即為合約B的地址,tx.origin 即為A的地址。



    若從一個地址直接對合約發起調用,那么 msg.sender 和 tx.origin 是一樣的,否則這兩個地址就不一樣。由于合約無法決定外部調用的關系,開發人員又往往容易混淆兩者的含義,進而留下隱患。


    以兩段真實的ERC20合約為例,第一段將 tx.origin 地址設為合約的owner地址,第二段將 msg.sender 設為合約的owner地址。在智能合約開發過程中,一定先搞明白代碼的實現意圖,再選擇使用 tx.origin 還是 msg.sender,使用 tx.origin 的時候要尤為慎重。


    依賴時間戳或者塊高度


    在 solidity 中,允許獲取當前時間戳(或者說交易所在區塊的區塊高度)。但是,這并不是安全的。一方面,時間戳是打包交易時候由礦工設置的,存在一定的人為操作因素在里面,礦工完全可以對時間戳做輕微的改動;另一方面,我們不能完全排除以太坊未來會在出塊時間上做出調整的可能性,因此通過塊高度來預估時間是存在一定隱患的。



    上述例子中,通過塊高度來限制ERC20代幣購買的時間段,假如在合約發布后,購買結束前的時間段內,以太坊平臺的出塊時間做出了調整,那么購買時效也會發生變動。


    另外,千萬不要使用這兩個值來產生隨機數。因為在合約外部一定范圍內(即同一個區塊內)是可以獲取到時間戳和塊高度的,所以對于同一個區塊中的合約來說,這兩個值就變得不隨機了,這便給黑客留下了可乘之機。

    整數相除


    在solidity中,整數相除遵循向下取整的原則。因此,當遇到不能整除的情況時,一定要謹慎處理。



    如上述代碼所示,計算結果的關系是`a == b * c + a % b`,而并非`a == b*c`。在ERC20合約開發過程中,經常會遇到使用除法的場景,要當心除法取整的問題,避免引起數據前后不一致的麻煩。


    關鍵字過時


    隨著solidity不斷的升級更新,老版本上的一些用法也逐漸被標記為過時然后廢棄掉。因此在合約開發和升級過程中,一定要當心過時的用法,避免造成不必要的損失。下表展示了部分過時用法和其替代用法可供參考。



    智能合約掌握著巨額的經濟價值,其影響力之大,波及范圍之廣可見一斑。哪怕一個很不起眼的小問題,都有可能造成不可挽回的經濟損失,這對大多數的項目來說無疑是滅頂之災。因此,智能合約的開發一定要慎之又慎,不要忽略任何細枝末節。

    來源:安比實驗室SECBIT

    版權申明:本內容來自于互聯網,屬第三方匯集推薦平臺。本文的版權歸原作者所有,文章言論不代表鏈門戶的觀點,鏈門戶不承擔任何法律責任。如有侵權請聯系QQ:3341927519進行反饋。
    相關新聞
    發表評論

    請先 注冊/登錄 后參與評論

      回頂部
    • <option id="cacee"><noscript id="cacee"></noscript></option>
    • <table id="cacee"><noscript id="cacee"></noscript></table>
    • <td id="cacee"></td>
    • <option id="cacee"></option>
    • <table id="cacee"></table>
    • <option id="cacee"><option id="cacee"></option></option>
    • <table id="cacee"><source id="cacee"></source></table><td id="cacee"><rt id="cacee"></rt></td>
      <option id="cacee"><option id="cacee"></option></option>
      妖精视频