審核智能合約以及安全問題

智能合約審計是一個仔細(xì)調(diào)查一段代碼的過程匾竿,在這種情況下是一個solidity合約,用于在代碼部署之前發(fā)現(xiàn)錯誤狈孔、漏洞和風(fēng)險食店。

審計的結(jié)構(gòu):

  1. 免責(zé)申明:在此您可以說審計不是具有法律約束力的文件,并且不保證任何內(nèi)容颖对。這只是一個討論文件捻撑。
  2. 審計和優(yōu)秀功能概述:快速查看將要審計的智能合約以及找到良好實踐。
  3. 對合約的攻擊:在本節(jié)中缤底,您將討論對合約和結(jié)果所做的攻擊顾患。只是為了驗證它確實是安全的。
  4. 合約中發(fā)現(xiàn)的嚴(yán)重漏洞:可能嚴(yán)重?fù)p害合約完整性的關(guān)鍵問題个唧。一些允許攻擊者竊取ether的錯誤是一個關(guān)鍵問題江解。
  5. 合約中發(fā)現(xiàn)的中等漏洞:可能損害合約但存在某種限制的漏洞。就像一個允許人們修改隨機變量的bug徙歼。
  6. 發(fā)現(xiàn)低嚴(yán)重性漏洞:這些漏洞并未真正損害合約膘流,并且可能存在于合約的部署版本中。
  7. 逐行注釋:在本節(jié)中鲁沥,您將分析最重要的行呼股,您可以看到潛在的改進。
  8. 審計摘要:您對合約的意見和有關(guān)審計的最終結(jié)論画恰。

免責(zé)申明

審計不對代碼的實用性彭谁、代碼的安全性、業(yè)務(wù)模型的適用性允扇、業(yè)務(wù)模型的監(jiān)管制度缠局、或任何其他關(guān)于合約適用性或其無bug狀態(tài)的陳述作出陳述或保證。審核文檔僅供討論之用考润。

常見的攻擊類型以及注意事項

  • 私人信息和隨機性:在智能合約使用的所有內(nèi)容都是公開可見的狭园,甚至是標(biāo)記的局部變量和狀態(tài)變量private。如果你不希望礦工能夠作弊糊治,在智能合約中使用隨機數(shù)是非常棘手的唱矛。
  • Re-Entrancy(重入):為避免重新入侵,您可以使用Checks-Effects-Interactions模式
call.value()如果用戶balance在發(fā)送ether之前沒有更新發(fā)送方井辜,
則此攻擊包括遞歸調(diào)用ERC20 token中的方法以提取存儲在合約上的ether绎谦。

? if (msg.sender.call.value(shares[msg.sender])(""))
            shares[msg.sender] = 0;
? if (msg.sender.send(shares[msg.sender]))
            shares[msg.sender] = 0; 
?? uint share = shares[msg.sender];
   shares[msg.sender] = 0;
   msg.sender.transfer(share);                   
  • 變量過大和過低:當(dāng)超出類型變量uint256的限制2**256時發(fā)生溢出。會發(fā)生什么粥脚?是該值重置為零而不是遞增更多窃肠。建議使用像OpenZeppelin的SafeMath.sol這樣的庫。
import './SafeMath.sol';
contract Casino {
    using SafeMath for uint256;
    function example(uint256 _value) {
        uint number = msg.value.add(_value);
    }
}
  • Replay(重放攻擊):它不再是問題刷允,因為Geth版本1.5.3和Parity 1.4.4都實現(xiàn)了Vitalik Buterin的攻擊保護EIP155
  • Reordering attack(重組攻擊):這種攻擊在于冤留,礦工或其他方試圖通過將自己的信息插入列表或映射來與智能合約參與者“競賽”碧囊,以便攻擊者可以幸運地將他們自己的信息存儲在合約中。
  • 短地址攻擊:此攻擊影響ERC20 token纤怒。購買令牌時呕臂,請務(wù)必檢查地址的長度。合約不易受此攻擊肪跋,因為它不是ERC20 token。參考:https://vessenes.com/the-erc20-short-address-attack-explained/
  • assert()require()行為幾乎完全相同土砂,但assert()用于在進行更改后驗證合約狀態(tài)州既,而require()通常用于函數(shù)頂部以驗證函數(shù)的輸入。
  • 調(diào)用堆棧深度:1024萝映。外部函數(shù)調(diào)用可能會隨時失敗吴叶,在這種情況下,Solidity會拋出異常序臂。惡意攻擊者可能會在與您的合約交互之前強制調(diào)用堆棧達到較高值蚌卤。請注意,如果調(diào)用堆棧耗盡奥秆,.send()則不會拋出異常逊彭,而是false在這種情況下返回。低級別的功能.call()构订,.callcode()以及.delegatecall()行為以同樣的方式侮叮。
  • 切勿使用tx.origin進行授權(quán)。

安全建議

外部調(diào)用

  1. 盡量避免外部調(diào)用:調(diào)用不受信任的外部合約可能會引發(fā)一系列意外的風(fēng)險和錯誤悼瘾。外部調(diào)用可能在其合約和它所依賴的其他合約內(nèi)執(zhí)行惡意代碼囊榜。

  2. 仔細(xì)權(quán)衡send()transfer()亥宿、call.value():在需要對外未知地址轉(zhuǎn)賬Ether時使用send()transfer()卸勺,已知明確內(nèi)部無惡意代碼的地址轉(zhuǎn)賬Ether使用call.value()

    • x.transfer(y)if(!x.send(y)) throw;是等價的烫扼。send是transfer的底層實現(xiàn)曙求,建議盡可能直接使用transfer。
    • someAddress.send()someAddress.transfer()能保證可重入安全映企。盡管這些外部智能合約的函數(shù)可以被觸發(fā)執(zhí)行圆到,但補貼給外部智能合約的2,300 gas,意味著僅僅只夠記錄一個event到日志中卑吭。
    • someAddress.call.value()將會發(fā)送指定數(shù)量的Ether并且觸發(fā)對應(yīng)代碼的執(zhí)行芽淡。被調(diào)用的外部智能合約代碼將享有所有剩余的gas,通過這種方式轉(zhuǎn)賬是很容易有可重入漏洞的豆赏,非常不安全挣菲。
  3. 處理外部調(diào)用錯誤:如果你選擇使用底層方法富稻,如call()callcode()白胀、delegatecall()等椭赋,一定要檢查返回值來對可能的錯誤進行處理。

     // bad
    someAddress.send(55);
    someAddress.call.value(55)(); // this is doubly dangerous, as it will forward all remaining gas and doesn't check for result
    someAddress.call.value(100)(bytes4(sha3("deposit()"))); // if deposit  throws an exception, the raw call() will only return false and transaction will NOT be reverted
    
     // good
    if(!someAddress.send(55)) {
    // Some failure code
    }
    
    ExternalContract(someAddress).deposit.value(100);
    
  4. 不要假設(shè)你知道外部調(diào)用的控制流程:無論是使用raw calls或是contract calls或杠,如果這個ExternalContract是不受信任的都應(yīng)該假設(shè)存在惡意代碼哪怔。即使ExternalContract不包含惡意代碼,但它所調(diào)用的其他合約代碼可能會包含惡意代碼向抢。一個具體的危險例子便是惡意代碼可能會劫持控制流程導(dǎo)致競態(tài)认境。

  5. 對于外部合約優(yōu)先使用pull 而不是push:外部調(diào)用可能會有意或無意的失敗。為了最小化這些外部調(diào)用失敗帶來的損失挟鸠,通常好的做法是將外部調(diào)用函數(shù)與其余代碼隔離叉信,最終是由收款發(fā)起方負(fù)責(zé)發(fā)起調(diào)用該函數(shù)。

  6. 標(biāo)記不受信任的合約:當(dāng)你自己的函數(shù)調(diào)用外部合約時艘希,你的變量硼身、方法、合約接口命名應(yīng)該表明和他們可能是不安全的覆享。

其他

  1. 使用assert()強制不變性:當(dāng)斷言條件不滿足時將觸發(fā)斷言保護 -- 比如不變的屬性發(fā)生了變化佳遂。
  2. 正確使用assert()和require()require(condition)被用來驗證用戶的輸入,如果條件不滿足便會拋出異常撒顿,應(yīng)當(dāng)使用它驗證所有用戶的輸入讶迁。assert(condition)在條件不滿足也會拋出異常,但是最好只用于固定變量:內(nèi)部錯誤或你的智能合約陷入無效的狀態(tài)核蘸。
  3. 小心整數(shù)除法的四舍五入:所有整數(shù)除數(shù)都會四舍五入到最接近的整數(shù)巍糯。 如果您需要更高精度,請考慮使用乘數(shù)客扎,或存儲分子和分母祟峦。
  4. 記住Ether可以被強制發(fā)送到賬戶:謹(jǐn)慎編寫用來檢查賬戶余額的不變量。
  5. 不要假設(shè)合約創(chuàng)建時余額為零:攻擊者可以在合約創(chuàng)建之前向合約的地址發(fā)送wei徙鱼。合約不能假設(shè)它的初始狀態(tài)包含的余額為零宅楞。
  6. 記住鏈上的數(shù)據(jù)是公開的:許多應(yīng)用需要提交的數(shù)據(jù)是私有的,直到某個時間點才能工作袱吆。如果你的應(yīng)用存在隱私保護問題厌衙,一定要避免過早發(fā)布用戶信息。
  7. 權(quán)衡Abstract合約和Interfaces:Interfaces和Abstract合約都是用來使智能合約能更好的被定制和重用绞绒。Interfaces和Abstract合約很像但是不能定義方法只能申明婶希。Interfaces存在一些限制比如不能夠訪問storage或者從其他Interfaces那繼承,通常這些使Abstract合約更實用蓬衡。盡管如此喻杈,Interfaces在實現(xiàn)智能合約之前的設(shè)計智能合約階段仍然有很大用處彤枢。另外,需要注意的是如果一個智能合約從另一個Abstract合約繼承而來那么它必須實現(xiàn)所有Abstract合約內(nèi)的申明并未實現(xiàn)的函數(shù)筒饰,否則它也會成為一個Abstract合約缴啡。
  8. 在雙方或多方參與的智能合約中,參與者可能會“脫機離線”后不再返回:不要讓退款和索賠流程依賴于參與方執(zhí)行的某個特定動作而沒有其他途徑來獲取資金瓷们。
  9. 使Fallback函數(shù)盡量簡單:謹(jǐn)慎編寫fallback函數(shù)以免gas不夠用业栅。
  10. 明確標(biāo)明函數(shù)和狀態(tài)變量的可見性:明確標(biāo)明函數(shù)和狀態(tài)變量的可見性。函數(shù)可以聲明為 external谬晕,public碘裕,internalprivate
  11. 將程序鎖定到特定的編譯器版本:智能合約應(yīng)該應(yīng)該使用和它們測試時使用最多的編譯器相同的版本來部署固蚤。鎖定編譯器版本有助于確保合約不會被用于最新的可能還有bug未被發(fā)現(xiàn)的編譯器去部署。智能合約也可能會由他人部署歹茶,而pragma標(biāo)明了合約作者希望使用哪個版本的編譯器來部署合約夕玩。
  12. 小心分母為零 (Solidity < 0.4)
  13. 區(qū)分函數(shù)和事件:為了防止函數(shù)和事件(Event)產(chǎn)生混淆,命名一個事件使用大寫并加入前綴(我們建議LOG)惊豺。對于函數(shù)燎孟,始終以小寫字母開頭,構(gòu)造函數(shù)除外尸昧。
  14. 使用Solidity更新的構(gòu)造器

開發(fā)理念

  1. 對可能的錯誤有所準(zhǔn)備
    • 有效的途徑來進行bug修復(fù)和功能升級
  2. 謹(jǐn)慎發(fā)布智能合約
    • 階段性發(fā)布揩页,每個階段都提供足夠的測試
  3. 保持智能合約簡潔
    • 確保智能合約邏輯簡潔
    • 確保合約合函數(shù)模塊化
    • 使用已經(jīng)被廣泛使用的合約或工具(比如,不要自己寫一個隨機生成器)
    • 條件允許的話烹俗,清晰明了比性能更重要
    • 只在你系統(tǒng)的去中心化部分使用區(qū)塊鏈
  4. 保持更新
    • 在任何新的漏洞被發(fā)現(xiàn)時檢查你的智能合約
    • 盡可能快的將使用到的庫或者工具更新到最新
    • 使用最新的安全技術(shù)
  5. 清楚區(qū)塊鏈的特性
    • 特別小心針對外部合約的調(diào)用爆侣,因為你可能執(zhí)行的是一段惡意代碼然后更改控制流程
    • 清楚你的public function是公開的,意味著可以被惡意調(diào)用幢妄。(在以太坊上)你的private data也是對他人可見的
    • 清楚gas的花費和區(qū)塊的gas limit

開發(fā)建議

  1. 嚴(yán)肅對待:如果編譯器警告你某事兔仰,應(yīng)該更好地改變它。
  2. 限制ether的數(shù)量:限制可以存儲在智能合約中的ether(或其他token)的數(shù)量蕉鸳。如果您的源代碼乎赴、編譯器或平臺有錯誤,這些資金可能會丟失潮尝。
  3. 保持簡潔而且模塊化
  4. 使用Checks-Effects-Interactions模式
  5. 包含故障安全模式:如果自檢失敗榕吼,合約會自動切換到某種“故障保護”模式,例如勉失,禁用大多數(shù)功能羹蚣,將控制交給固定和可信任的第三方,或者只是將合約轉(zhuǎn)換為簡單的“把我的錢還給我“合約乱凿。
  6. 規(guī)范驗證:證明源代碼符合某種正式規(guī)范度宦。

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末踢匣,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子戈抄,更是在濱河造成了極大的恐慌离唬,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件划鸽,死亡現(xiàn)場離奇詭異输莺,居然都是意外死亡,警方通過查閱死者的電腦和手機裸诽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門嫂用,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人丈冬,你說我怎么就攤上這事嘱函。” “怎么了埂蕊?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵往弓,是天一觀的道長。 經(jīng)常有香客問我蓄氧,道長函似,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任喉童,我火速辦了婚禮撇寞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘堂氯。我一直安慰自己蔑担,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布咽白。 她就那樣靜靜地躺著钟沛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪局扶。 梳的紋絲不亂的頭發(fā)上恨统,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音三妈,去河邊找鬼畜埋。 笑死,一個胖子當(dāng)著我的面吹牛畴蒲,可吹牛的內(nèi)容都是我干的悠鞍。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼咖祭!你這毒婦竟也來了掩宜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤么翰,失蹤者是張志新(化名)和其女友劉穎牺汤,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浩嫌,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡檐迟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了码耐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片追迟。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖骚腥,靈堂內(nèi)的尸體忽然破棺而出敦间,到底是詐尸還是另有隱情,我是刑警寧澤束铭,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布廓块,位于F島的核電站,受9級特大地震影響纯露,放射性物質(zhì)發(fā)生泄漏剿骨。R本人自食惡果不足惜代芜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一埠褪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧挤庇,春花似錦钞速、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至昆咽,卻和暖如春驾凶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背掷酗。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工调违, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人泻轰。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓技肩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親浮声。 傳聞我的和親對象是個殘疾皇子虚婿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容