以太坊智慧型合約安全

2021-09-22 22:27:25 字數 4433 閱讀 3322

智慧型合約就是自主執行的合約,其條款是用**規定的。

雖然這個概念已經存在一段時間了,但至少從2023年nick szabo提出了這一概念以來,直到圖靈完備的以太坊區塊鏈來臨,智慧型合約的使用才變得普遍。

對智慧型合約理念的字面解釋造成了「**即法律」 ( 「code is law」 ) 的正規化理解,意思是那些智慧型合約具有約束力,並被詮釋為合法檔案。所有意識到無法建立完全無錯**的軟體工程師在想到電腦程式具有合法約束力的時候,手心都會捏一把汗。這裡存在很多明顯的問題:

**有漏洞。寫無bug**是一件非常困難的事情,即使做好了所有可能的預防措施,在相當複雜的軟體中也總會存在意想不到的執行路徑或者可能的漏洞。

法律合約也需要解釋與仲裁。建立毫無漏洞法律合約是非常困難的。在任何大型合約中,拼寫錯誤可能出現,一些條款需要解釋和仲裁。這就是為什麼在出現爭議時我們需要法庭。如果在某個法律合約中,40頁中有39頁標定的銷售**為100美金,而在某一頁上的**中多了個「0」,法院將以「合約精神」為準進行裁定。而計算機只會執行寫好的條款,區塊鏈的不變性增加了這個問題,因為合約無法輕易修改。

軟體工程師不是法律專家,反之亦然。起草乙份好的合約需要與程式設計不同的技能,一名能夠編寫非常完善電腦程式的人員不一定會寫法律合約。

這件事很多人都早已談了許多,我們就不在這重複了。您可以在這裡找到關於攻擊和善後的完整概述。

簡單來說,在 2016 年 6 月,一名黑客企圖轉移一大筆眾籌資金 (350 萬個 eth, 佔當時eth總數的15%)至他自己的子合約,這筆資金被鎖定在該子合約中 28 天,因而大家要與時間競賽尋找解決方案。

這事件要注意的重點在於,攻擊者是通過使合約以意料之外的方式執行而發起的攻擊。這個事件中,攻擊者利用了 可重入漏洞( reentrancy vulnerabilities )。我們將會在這篇文章中對可重入進行深入討論。

事實上,這是第二次 parity 所提供的的多重簽名錢包合約受到黑客入侵了。眾多創業公司使用的多重簽名錢包的邏輯大多通過庫合約實現。每個錢包都包含乙個輕量級的客戶端合約,連線到這個單點故障(譯註:即上述庫合約)。

這個庫合約中存在乙個重大的漏洞,問題在於其中乙個初始化函式只能被呼叫一次。

2023年11月,一名男子通過實施合約初始化,將自己變成了合約所有者。這允許他呼叫所有者才能呼叫的 函式,他利用這一特權呼叫了以下的函式:

// kills the contract sending everything to `_to`.

function kill(address _to) onlymanyowners(sha3(msg.data)) external

這相當於乙個能使合約無效的自毀按鈕。呼叫這個函式會導致客戶合約的所有資金有可能被永久凍結。

直到撰寫本文之時,該事件到底是黑客造成的故意攻擊還是意外仍然是個謎,儘管肇事者聲稱這是意外行為。

兩次攻擊都說明了即使是由以太坊生態系統的大佬來寫相對簡單的合約,也容易出現基本的錯誤,帶來嚴重的後果。

使用不安全的私鑰純粹是使用者的失誤,而不是乙個漏洞。然而,儘管如此,我們還是要指出來,因為私鑰外洩總是意外地發生,有些玩家專門從不安全的位址中竊取資金。

把開發位址(如 ganache/testprc 使用的位址)用於生產的事情經常發生。這些位址是由公開的私鑰生成。一些使用者甚至無意識地把這些私鑰匯入到錢包軟體,因為他們使用 ganache 的種子詞(seed words)生成了相同的私鑰。攻擊者則監視著 testprc 位址,不管多少數量的以太幣只要轉移到以太坊主鏈上的 testprc 位址都會立刻消失(在2個區塊內)。一項有趣的研究對這一十分有利可圖的「清掃(sweeping)」活動進行了調查,並發現每個清掃者(sweeper)的賬戶都設法累積了高達2300萬美元的資金。

如果乙個函式在執行完成前被呼叫了數次,發生意料不到的行為時,可重入漏洞就可能出現。

讓我們看看下面這個函式,它可以用於從合約中提取呼叫者的總餘額:

function payout ()

呼叫 call.value() 會導致合約外部**的執行。若呼叫者是另乙份合約,這就意味著合約回退措施的執行。這可能會在餘額調整為 0 之前再次呼叫 payout(), 從而獲得比可用資金更多的資金。

這種情況下的解決方法就是使用替代函式 send() 或 transfer()

,後兩者函式能為基礎運作提供足夠的 gas,而想要再次呼叫 p*ayout()* 時 gas 就不足了。

若合約含有兩個共享狀態的函式,那麼不需要重複呼叫函式也可能會發生相似的競態條件(race conditions)。因此,最好的做法是在轉賬前更改狀態,即轉移資金前,在上述**中把餘額設為 0。

the dao 攻擊利用了該漏洞的一種變體。

餘額一般用無符號的整數表示,在 solidity 語言中通常為 256 位數字。當無符號整數上溢(overflow)或下溢(underflow)時,其數值會發生明顯變化。讓我們看看下面乙個比較常見的下溢例子 (為了清晰一點我把數字縮短了):

0x0003

- 0x0004

--------

0xffff

這裡很容易看出問題,減去乙個比可用餘額大的值便導致下溢。得到的餘額是乙個很大的數字。

還要注意的是由於捨入誤差(rounding errors),在整數中算術分割(arithmetics division)是很麻煩的。

解決方法是時刻對**進行下溢、上溢檢查。使用安全數字庫能協助檢查,比如 openzeppelin 的 safemath

。交易進入未確認的交易池,並可能被礦工無序地包含在區塊中,這取決於礦工的交易選擇標準,有可能是一些旨在從交易費中獲取最大收益的演算法,但也可以是其它任何標準。因此,打包在區塊中的交易順序與交易生成的順序完全不同。因此,合約**無法對交易順序作出任何假設。

因為交易在記憶池(mempool)是可見的,其執行是可**的,所以除了合約執行出現意外結果的情況,還有乙個可能的攻擊面。交易打包中可能出現的乙個問題就是,延遲某個交易可能被流氓礦工用作個人利益。事實上,能夠在交易執行前意識到某些交易(的存在)對任何人來說都是有利的,而不僅僅是礦工。

時間戳(timestamps)是由礦工生成。因此,合約不應該讓關鍵操作依賴於區塊時間戳,例如把時間戳用作乙個生成隨機數的種子。consensys 在他們的指導手冊中給出了「12分鐘規定」,表明如果你依賴時間戳的**能夠處理 12 分鐘的誤差,那麼使用block.timestamp 是安全的。

golem team 揭露了乙個有趣的攻擊,詳情請看這裡。該漏洞影響了 erc20 代幣傳輸和一些類似的合約,該漏洞的問題在於交易位元組**可以是任意大小,而以太坊虛擬機器(ethereum virtual machine,簡稱evm)會在其尾部缺失的位元組填充0。

實施該攻擊需要找到乙個以十六進製制(hex)形式表示且結尾為若干個 0 的位址,並在提幣請求中省略這些結尾的 0。當該合約發起乙個轉賬請求時,短位址被插入,其餘的交易位元組**被移位。

舉個例子,省略結尾的兩個 0 會導致交易資料中位址之後的位元組發生 1 個位元組的移位。位址後面是交易資料中的引數,通常是無符號的前置 0 的 256 位整數。這些前置的 0 會移入位址字段,使位址有效並確保交易目的地是正確的。

引數欄位中乙個位元組的移位也很容易導致提幣量變為原來的256倍。在evm用 0 填充缺失的結尾位元組後,交易成功,然後轉走 256 倍的金額。

因此,利用省略兩個十六進製制0的位址的漏洞使攻擊者可以從乙個餘額為 1000 個代幣的賬戶中提取 256000 個代幣,以此類推。省略 4 個結尾的 0 則是 2^16 倍。

為了避免這種攻擊,你的合約應該驗證位址。

有時通過使合約交易超過能夠包含在乙個區塊中的最大 gas 量,來迫使合約交易失敗。在這篇拍賣合約的解讀中解釋了這個經典例子。迫使合約退還大量沒有接受的小投標會增加 gas 消耗量,如果能耗超過了區塊 gas 上限,那麼整個交易失敗。

這個問題的解決方法是避免許多交易呼叫可能由相同的函式呼叫引起的情況,尤其是如果呼叫次數會受到外部影響。

推薦的付款模式是讓客戶請求轉賬,而不是一次性轉賬出去,如 solidity 官方檔案所述。

為了強調「**即法律」這一正規化理解的危害,本文我們闡述了可能發生的漏洞以及過去攻擊者是如何利用這些漏洞的例子。

最近的歷史事件表明在公鏈上執行圖靈完備的智慧型合約是危險的,其安全性遠不足以取代傳統法律系統的語言準確度與解釋和仲裁空間。

但這並不意味著我們應該拋棄智慧型合約。智慧型合約是非常有用的工具,能開發出有趣的應用程式。然而,我們不能認為智慧型合約能取代具有法律約束力的合約,它只是用於自動化的補充工具。

另外,我們應該做好預防措施去避免漏洞:

智慧型合約 以太坊

智慧型合約是執行在可複製 共享的賬本上的電腦程式,可以處理資訊,接收 儲存和傳送價值。2.1 什麼是以太坊 以太坊 ethereum 是乙個分布式計算機,有許多的節點,其中的每乙個節點都會執行智慧型合約,然後把結果存在區塊鏈上。由於整個網路是分布式的,且應用就是乙個個的狀態組成,儲存了狀態就有了服務...

以太坊和智慧型合約

1 什麼是以太坊?以太坊的官方 告訴我們 以太坊是乙個執行著智慧型合約的分布式平台 應用程式完全按照程式執行,不存在故障 審查 欺詐或第三方干預的可能性 2 智慧型合約就是可以處理資金的指令碼。開發語言 solidity 整合開發工具ide remix,乙個基於瀏覽器的整合開發環境 開發框架 tru...

以太坊智慧型合約部署代幣

pragma solidity 0.4.0 建立乙個基礎合約,用於指定某些操作只能由合約擁有者 執行 contract owned 宣告乙個修改器,證明只有合約擁有者才能執行某些操作 modifier onlyownerelse 此函式的功能是把合約轉讓給指定使用者 function transfe...