盤他!以太坊合約的安全性弱點II

今天,我們繼續介紹文獻中提到的以太坊合約的安全性弱點與案例。

數據保密
Solidity 智能合約中的變量分 public 和 private 兩種。Private 變量表示這個數值不能被其他合約直接讀取。

但是,將一個變量標記爲 private 不意味着裏面的信息就是保密的。因爲以太坊是公開的,每次合約函數執行的字節碼,參數都是公開的,任何人都可以推斷出每次函數執行後是否修改了變量,修改後的數值是多少。Private 只是能夠保證其他合約執行的時候,無法讀取其中的變量罷了。

然而,一些本應該隱藏玩家數據的遊戲,卻錯誤地使用了這一點。以下是一個示例:

clipboard.png

兩個玩家在玩一個賭博遊戲,每個玩家選一個正整數,兩個正整數加起來的奇偶性,決定了獲勝者。

存儲玩家選擇的變量是 players, 這個變量具有 private 屬性。玩家通過調用智能合約函數選數的時候,決策並不是保密的。

爲了實現保密性,更好的方式是使用一個稱爲“委託”的密碼學原語。當玩家需要祕密地做出一個決定時,可以將 sha3(決策內容,隨機數) 得到的哈希值存到合約裏。當玩家需要公開自己選擇的時候,將 決策內容 與 隨機數 公開,並由智能合約驗證哈希值與先前存儲的是否一致。可以實現類似於“先在紙上祕密寫下選擇,到後面一個環節再亮出來”的功能。

隨機數生成
EVM 的字節碼執行是確定性的。因此,如果智能合約想生成一個隨機數,就需要通過一個隨機數種子生成一個僞隨機數。而隨機數種子的選取方式,對生成隨機數的公平性有很大的影響。

一個常用的做法是,使用一個給定時間或給定高度區塊的哈希值或時間戳。這件事情在給定區塊被確認以後,在每個礦工看來都是一樣的。

看起來這是一個公平的做法,因爲沒有人能預測未來的區塊。但是,一個惡意的礦工可能嘗試操縱自己生成的區塊來影響隨機數的生成與合約的執行結果。一個分析表示,一個控制少數算力的礦工,只需要投入 50 個比特幣就可以顯著地改變輸出結果的概率分佈。

另一個方式是使用“延時委託協議”。在這個協議中,每個參與者選擇一個祕密的隨機數,並將哈希值廣播給其他人。在晚些時候,所有參與者公佈它們選取的祕密隨機數,或者丟失押金。最終隨機數由所有公佈的祕密隨機數和一個公開的算法生成。攻擊者可以在“其他所有人都已經公佈了隨機數,自己還沒有公佈”時,預先計算最終生成的隨機數,如果生成的結果它不滿意,就通過拒絕公佈自己選擇的隨機數方式,來使這個結果無效。當然,攻擊者要損失一些押金。所以,押金的設定要足夠高,高於隨機數生成中改變結果可能帶來的收益。

不可預測狀態
一個合約的狀態包括合約變量和合約餘額。一般情況下,當用戶通過一筆交易調用合約函數的時候,從交易廣播到交易被加入區塊之間,可能有其他的交易改變了合約的狀態。也就是說,當用戶發起一筆交易時,並不能確定這筆交易被執行時,合約的狀態是什麼。

一個基於 library 和不可預測狀態的攻擊
下面我們來看一個例子,以下的 Solidity 代碼定義了一個名爲 Set 的 library。

clipboard.png

下面是一個名爲 SetProvider 的合約,提供了一個 Set library 的地址,合約擁有者可以修改這個地址,任何人/合約可以獲取這個地址。

clipboard.png

假設 Bob 合約是一個使用 SetProvider 的誠實用戶,他的代碼如下:

clipboard.png

Bob 記錄了一個 SetProvider 合約的地址,在 getSetVersion() 中,使用這個地址獲取了一個 Set library 的版本號。

現在,假設 SetProvider 合約的控制者是個壞人。他製造了一個惡意的 Set library, 希望偷得一些錢存到他自己的錢包地址 0x42 中。

clipboard.png

如果合約 SetProvider 中 setLibAddr 的地址被修改爲 MaliciousSet 的地址, Bob 合約中調用 getSetVersion() 函數時,會調用 MaliciousSet 的 version() 函數,而不是 Set 的。因爲 Bob 合約中將 Set 聲明爲一個 library, 所以對 version() 的調用採用的是 delegatecall 模式。 delegatecall 模式意味着轉賬操作 attackerAddr.send(this.balance); 是從 Bob 合約中轉賬出來。 Bob 合約中的錢將被偷走。

上述例子說明了,對 library 函數的調用使用的是較爲危險的 delegatecall 模式。因此合約編寫者在使用其他合約地址作爲 library 時,一定要保證通過 library 加載進來的代碼是自己可控的。比如,手動指定一個已經在區塊鏈上不可修改的 library 的地址。而不是在本例中依靠於一個不可靠的 SetProvider 合約來獲取 library 的地址。

另外,“不可預測狀態”問題也加劇了這件事情。即使在調用 Bob 合約的 getSetVersion() 函數時, SetProvider 指向的是誠實的 Set library. 攻擊者也可以在這一交易還沒有被加入區塊的時候,通過發起一筆交易費數額較大的交易,搶在 getSetVersion() 被加入區塊之前,將 SetProvider 的指向修改爲 MaliciousSet。

Conflux 是致力於打造下一代高性能的 DAPP 公鏈平臺

歡迎關注我們的微信公衆號:Conflux中文社區(Conflux-Chain)

添加微信羣管理員 Confluxgroup 回覆“加羣”加入 Conflux官方交流羣

clipboard.png

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