今天,咱們繼續介紹文獻中提到的以太坊合約的安全性弱點與案例。算法
數據保密
Solidity 智能合約中的變量分 public 和 private 兩種。Private 變量表示這個數值不能被其餘合約直接讀取。安全
可是,將一個變量標記爲 private 不意味着裏面的信息就是保密的。由於以太坊是公開的,每次合約函數執行的字節碼,參數都是公開的,任何人均可以推斷出每次函數執行後是否修改了變量,修改後的數值是多少。Private 只是可以保證其餘合約執行的時候,沒法讀取其中的變量罷了。微信
然而,一些本應該隱藏玩家數據的遊戲,卻錯誤地使用了這一點。如下是一個示例:ide
兩個玩家在玩一個賭博遊戲,每一個玩家選一個正整數,兩個正整數加起來的奇偶性,決定了獲勝者。函數
存儲玩家選擇的變量是 players, 這個變量具備 private 屬性。玩家經過調用智能合約函數選數的時候,決策並非保密的。性能
爲了實現保密性,更好的方式是使用一個稱爲「委託」的密碼學原語。當玩家須要祕密地作出一個決定時,能夠將 sha3(決策內容,隨機數) 獲得的哈希值存到合約裏。當玩家須要公開本身選擇的時候,將 決策內容 與 隨機數 公開,並由智能合約驗證哈希值與先前存儲的是否一致。能夠實現相似於「先在紙上祕密寫下選擇,到後面一個環節再亮出來」的功能。區塊鏈
隨機數生成
EVM 的字節碼執行是肯定性的。所以,若是智能合約想生成一個隨機數,就須要經過一個隨機數種子生成一個僞隨機數。而隨機數種子的選取方式,對生成隨機數的公平性有很大的影響。this
一個經常使用的作法是,使用一個給定時間或給定高度區塊的哈希值或時間戳。這件事情在給定區塊被確認之後,在每一個礦工看來都是同樣的。spa
看起來這是一個公平的作法,由於沒有人能預測將來的區塊。可是,一個惡意的礦工可能嘗試操縱本身生成的區塊來影響隨機數的生成與合約的執行結果。一個分析表示,一個控制少數算力的礦工,只須要投入 50 個比特幣就能夠顯著地改變輸出結果的機率分佈。遊戲
另外一個方式是使用「延時委託協議」。在這個協議中,每一個參與者選擇一個祕密的隨機數,並將哈希值廣播給其餘人。在晚些時候,全部參與者公佈它們選取的祕密隨機數,或者丟失押金。最終隨機數由全部公佈的祕密隨機數和一個公開的算法生成。攻擊者能夠在「其餘全部人都已經公佈了隨機數,本身尚未公佈」時,預先計算最終生成的隨機數,若是生成的結果它不滿意,就經過拒絕公佈本身選擇的隨機數方式,來使這個結果無效。固然,攻擊者要損失一些押金。因此,押金的設定要足夠高,高於隨機數生成中改變結果可能帶來的收益。
不可預測狀態
一個合約的狀態包括合約變量和合約餘額。通常狀況下,當用戶經過一筆交易調用合約函數的時候,從交易廣播到交易被加入區塊之間,可能有其餘的交易改變了合約的狀態。也就是說,當用戶發起一筆交易時,並不能肯定這筆交易被執行時,合約的狀態是什麼。
一個基於 library 和不可預測狀態的攻擊
下面咱們來看一個例子,如下的 Solidity 代碼定義了一個名爲 Set 的 library。
下面是一個名爲 SetProvider 的合約,提供了一個 Set library 的地址,合約擁有者能夠修改這個地址,任何人/合約能夠獲取這個地址。
假設 Bob 合約是一個使用 SetProvider 的誠實用戶,他的代碼以下:
Bob 記錄了一個 SetProvider 合約的地址,在 getSetVersion() 中,使用這個地址獲取了一個 Set library 的版本號。
如今,假設 SetProvider 合約的控制者是個壞人。他製造了一個惡意的 Set library, 但願偷得一些錢存到他本身的錢包地址 0x42 中。
若是合約 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官方交流羣