在本系列中,我們將對以太坊現有的安全問題和前沿的各型別漏洞挖掘方法進行綜述,本文是本系列的第一篇文章,主要介紹以太坊的機制和存在的安全問題的分類,
01 什么是以太坊智能合約?
以太坊智能合約基于區塊鏈(Blockchain)技術,作為一種旨在以資訊化方式傳播、驗證或執行的計算機協議,為各類分布式應用服務提供了基礎,簡單來講:如果把位元幣看作是分布式的記賬本;以太坊便是可以運行程式的分布式計算平臺,程式運行的基礎則是Solidity智能合約,
智能合約早在1995年就由尼克薩博提出,目的在于以數字形式定義一個合同,當參與方執行且滿足合同所需的條件時,計算機自動執行該合同內容,但是由于技術條件等限制,這一概念當時并未實作,隨著近年來區塊鏈(Blockchain)技術的逐漸成熟以及加密貨幣的快速發展,由此進入了以智能合約技術為標志的區塊鏈2.0時代,
智能合約不只是一個可以在區塊鏈上被自動執行的程式,它本身就是一個系統參與者,可以對資訊進行接收和回應,也可以存盤和收發資產,支持智能合約運行的區塊鏈平臺很多,比如EOS,BCOS,Fabric,CITA等,其中規模最大,歷史最久也最具影響力的是以太坊(Ethereum),以太坊通過建立一個圖靈完備的,可以允許開發人員撰寫任意智能合約和去中心化應用(Dapps)的平臺而廣受歡迎,
02 為什么需要關注智能合約的安全問題
隨著智能合約應用場景的豐富,如金融、保險、游戲、能源等領域,其中控制的重要金融資產不可避免地成為吸引人攻擊的目標,此外,由于智能合約本身編程語言的不安全性以及合約整個生命程序的復雜性,再加上以太坊中每個用戶都可以在沒有可信賴第三方的情況下參與合約,都為智能合約的安全性帶來了巨大的風險和隱患,
比如2017年6月18日,智能合約The DAO中的可重入漏洞最終造成了億萬美元的損失,影響了整個以太坊網路,2020年1月16日,攻擊者通過開源專案為RVN加了功能,并通過一些條件判斷來繞過審查,從而擁有發幣功能,最侄訓利千萬人民幣,使得平臺遭遇巨大損失,
以太坊智能合約安全漏洞事件的頻繁爆發,造成了巨大的經濟損失,需要我們對其安全性檢查方面進行研究,
03 智能合約有著怎樣的架構
智能合約作為一段腳本,可以使用多種高級語言進行開發,比如Solidity,Serpent,Vyper等,其中針對以太坊的智能合約通常采用Solidity撰寫,其語法類似于JavaScript,智能合約的編譯和解釋執行則通過安裝在每個以太坊節點上的以太坊虛擬機(Ethereum Virtual Machine, EVM)來完成,根據以太坊白皮書,以太坊系統架構如圖1所示,每個以太坊節點架構自底向上分別是操作 系統、區塊鏈節點客戶端、以太坊虛擬機和智能合約腳本,

圖1 以太坊系統架構
以太坊正是通過運行在不同主機上的以太坊客戶端節點之間的通信來完成各種操作,撰寫好的合約首先通過EVM編譯器編譯生成一個ABI檔案和一個bin檔案,其中ABI檔案是合約的介面描述,包括了欄位名稱、欄位型別、引數名稱、引數回傳值等資訊,而bin檔案是最終運行在虛擬機上的位元組碼(bytecode),即一段EVM指令集合,
合約的部署是將編譯后的位元組碼上傳到以太坊區塊鏈平臺上,其程序與發送一筆交易類似,發起地址為發布者的地址,目標地址為零,交易資料被替換為合約對應的位元組碼,在進行交易打包時,將根據發布者的地址和交易序列號通過加密演算法重新計算出一個地址作為這個合約的地址,呼叫者可通過合約地址對合約進行呼叫,以太坊中存在兩類賬戶:外部賬戶和合約賬戶,外部賬戶由私鑰控制,有賬戶余額,可以觸發交易但沒有代碼;而合約賬戶則包含不可修改的智能合約代碼,有賬戶余額,但不能主動發起交易,只能在被觸發后執行預先撰寫的邏輯,
因此,合約呼叫也分為兩種型別,一種由外部賬戶發起稱為交易呼叫,另一種則是由一個合約發起對另一個合約的呼叫,稱為訊息呼叫,此外,智能合約還具有自毀操作,這也是唯一能從區塊鏈上將合約代碼移除的方式,它需要在合約撰寫中執行selfdestruct操作,之后合約賬戶上剩余的以太幣(Ether)會被發送給指定目標,其存盤的相關狀態和代碼也會被移除,而所謂以太幣是以太坊中的通用貨幣,類似于位元幣,
04 與傳統程式比,智能合約有哪些特殊機制?
gas機制
合約需要礦工的強制執行和證明,因此在打包一項交易時,即將其添加到一個區塊上時,為了避免交易中包含大量回圈等操作導致節點資源的浪費,合約需先行向礦工支付一筆費用(Gas),在以太坊執行一份合約本身也需要根據內部制定的規則消耗一定量的gas,如果交易執行后,提前支付的gas還有剩余,則按照原路返還;如果沒有執行結束gas就被耗盡,則會觸發一個out-of-gas例外,當前合約程式的所有執行狀態都會被回滾,但是因為礦工為了執行相應計算已經付出了算力,所以已經消耗的gas不會被退回,
委托呼叫機制
合約可以通過訊息呼叫的機制來呼叫其他合約,委托呼叫(delegatecall)就是一種特殊型別的訊息呼叫,它和普通的call指令的區別在于,普通call指令的行為是跳轉到被呼叫合約并在該合約中執行完相應代碼再回傳執行后續代碼,對于呼叫發起者背景關系無影響;而委托呼叫則相當于把被呼叫合約中的一段代碼拷貝到呼叫發起者合約的背景關系環境中執行,會對呼叫發起者中的資訊作出修改,
例外傳遞機制
智能合約的函式呼叫方式分為內部呼叫和外部呼叫,內部函式呼叫是指直接呼叫當前合約或者父合約的內部函式,這些函式呼叫在EVM中會被直接轉換為簡單的跳轉指令;而外部函式呼叫是指對于指定地址的外部合約函式的呼叫,需要靠訊息呼叫完成,其中外部呼叫中的一些低級呼叫如call,callcode,delegatecall在執行中如果出錯拋出例外,該例外不會沿著函式呼叫堆疊被傳遞,而是只能獲取一個布林值來表示成功或者失敗,此外,一些轉賬函式如call.value, send等,當發生轉賬例外時,也僅回傳一個布林值而不是回滾這個操作,
05 智能合約有哪些安全問題?
智能合約的原始碼公開透明雖然提升了用戶對合約的信任度,但也使每一份公開的合約都有可能成為黑客的攻擊目標,智能合約的可信度來源于其不可篡改性,但正因如此也使得智能合約無法像傳統程式那樣通過打補丁等措施修復漏洞,
本文接下來將對當前典型的智能合約漏洞型別進行總結,
根據Atzei等人的一份調查報告,智能合約的安全漏洞可以按照高級語言Solidity、EVM和區塊鏈三個層面進行分類,高級語言Solidity層面的漏洞主要為語言自身設計的缺陷以及開發者在開發程序中引入的錯誤,EVM層面的安全威脅主要由以太坊智能合約位元組碼規范和運行機制本身的一些缺陷帶來,區塊鏈層面的問題是由區塊鏈本身的很多特性引入,具體如表1中所列,
表1 漏洞分類
| 分類 | 安全漏洞 |
|---|---|
| 高級語言Solidity | 整數型別錯誤 未校驗回傳值 權限控制問題 拒絕服務 資產凍結 |
| EVM | 重入漏洞 短地址攻擊 代碼注入 |
| 區塊鏈 | 交易順序依賴漏洞 時間戳依賴漏洞 可預測的隨機處理 |
可以采取一些措施規避潛在的重入漏洞:先保證改變狀態變數的邏輯發生后再允許以太幣從合約中轉出去;或將以太幣發送至外部合約時,使用內置的transfer函式代替call、send函式實作安全的轉幣操作,
06 智能合約安全漏洞介紹
1)整數型別錯誤
整數型別錯誤主要包含算數錯誤、截斷錯誤和符號錯誤,算術錯誤包括整數溢位、除數為零和模數為零這三種錯誤,EVM使用幾種固定的長度來表示整數,這代表只能表示一定范圍內的數字,一旦整數運算結果超出這個范圍就會發生整數溢位,攻擊者可以利用整數溢位漏洞跳過某些條件判斷或者篡改資料,著名的BEC漏洞事件就是由于在轉賬程序中發生了整數溢位導致了巨額的代幣蒸發,使用提供安全檢查的SafeMath數學計算庫可以有效避免溢位漏洞,在EVM和Solidity舊版本中,除數為零和模數為零只會導致運算結果為0,并不會觸發例外,截斷錯誤是指將一個整數型別資料轉換為寬度更短的整數型別資料導致精度的丟失,符號錯誤是指將一個有符號整數型別資料轉換為相同寬度的無符號整數型別資料,可能會導致一個負數變為一個很大的整數,
2)未校驗回傳值
以太坊Solidity語言的函式呼叫和其他高級語言一樣一般都會設定回傳值,但是對send、call、delegatecall等低級別函式呼叫失敗時不會引起事務回滾操作,而只是在回傳值中表示是否發生了例外,攻擊者可以通過故意發送失敗的操作來導致程式執行與預期設定不同,從而造成智能合約的狀態混亂,開發者在開發合約時可以通過對低級別函式呼叫的回傳值校驗來確定呼叫是否成功,從而確保合約能以預先設定的邏輯執行,
3)權限控制問題
Solidity中可以使用四種說明符設定函式和狀態變數的可見性,分別為external、internal、public和private,未宣告可見性的函式會被默認為public,即該函式不僅允許內部呼叫還會作為合約對外介面被外部合約呼叫,如果一些涉及到轉賬等敏感操作的函式沒有宣告可見性,就可能使攻擊者有機可乘,
4)拒絕服務
拒絕服務(Denial-of- Service, DoS)一般指不可恢復的惡意操作或者可控制的無限資源消耗,針對以太坊合約的 DoS 攻擊會導致以太幣和Gas的大量消耗,更嚴重則會導致永久性地無法使用合約,2016年的以太坊游戲The King of the Ether Throne, 攻擊者利用了外部函式呼叫的漏洞,并通過(Unexpected)Revert 發動DoS攻擊,導致該游戲運營出現重大問題,
5)資產凍結
由于合約的不可篡改性,如果開發者在進行智能合約開發時,僅設定了接收以太幣的功能,但沒有設定任何允許以太幣轉出的操作,或由于某些原因導致以太幣無法轉出,就會導致合約內的資產被永久凍結,
6)重入漏洞
雖然以太坊智能合約的執行是一個具有原子性和順序性的事務操作,但是當用戶在呼叫智能合約時,如果被呼叫合約沒有找到被呼叫的函式或者該合約只接收到以太幣而沒有其他任何訊息時就會呼叫回退函式(fallback),攻擊者可以通過構造特殊的回退函式來攻擊存在重入漏洞的智能合約,例如回退函式中包含重新呼叫被攻擊合約中之前向攻擊者轉賬函式的代碼,從而實作遞回呼叫并耗盡被攻擊合約的資產,著名的導致以太坊硬分叉(ETH/ETC)的 The DAO 事件就跟重入漏洞有關,
7)短地址攻擊
短地址攻擊漏洞是一種由于未校驗用戶輸入導致的漏洞,攻擊者利用虛擬機的自動補全機制,構造末尾為零的地址進行合約呼叫,并在傳入引數時故意將地址(address)末尾的零省去,虛擬機會取發送代幣的金額(amount)高位的0對地址補全,同時會將amout低位補0,這樣就等效于amount左移翻倍,導致轉移的代幣數量超出了原來的設定,通過嚴格檢查用戶輸入,拒絕接受畸形地址,可以有效避免短地址攻擊,
8)代碼注入
代碼注入漏洞由以太坊的委托呼叫機制引入,委托呼叫機制中使用的delegatecall指令允許合約在自己的背景關系執行其他合約的代碼片段,攻擊者可以利用該漏洞向合約注入修改合約中重要狀態變數等惡意操作的代碼,
9)交易順序依賴漏洞
一筆交易被傳播出去并被礦工認同寫入一個區塊內需要一定的時間,攻擊者可以通過監視網路上依賴于交易順序的合約,并通過發出他自己的交易來改變當前的合約狀態,例如,攻擊者可以提交一個懸賞合約,允許用戶通過提交難題的答案從該合約獲得豐厚的獎勵,攻擊者可以在提交完懸賞合約后持續監聽網路,若有人提交了答案并且此時提交答案的交易還未被確認,攻擊者可以立刻發起一個將獎金降低到無限接近于0的交易并提供較高的gas使自己的交易先被礦工處理,提交答案的交易后被處理,這樣攻擊者支付很少的獎金就可以獲得問題的答案,
10)時間戳依賴漏洞
一些智能合約可以通過引數block.timestamp獲得當前區塊的時間戳并將其作為判斷依據,而時間戳一定范圍內可以由礦工來決定,因此,一般情況下認為智能合約中依賴于時間戳的代碼若可以允許12分鐘的誤差,那么該種使用時間戳的方式就是安全的,
11)可預測的隨機處理
合約開發者撰寫亂數生成函式時,有時會利用時間戳(block.timestamp)、區塊號(block.number)等與區塊有關的一些引數產生亂數,但是區塊鏈上的上述資料都是公開的,這使得生成的亂數是可預測的,從而可能會被攻擊者利用,智能合約的開發人員可以使用例如Oraclize等第三方服務獲取亂數來避免該問題,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/303361.html
標籤:區塊鏈
