2024智能合約安全指南_第1頁
2024智能合約安全指南_第2頁
2024智能合約安全指南_第3頁
2024智能合約安全指南_第4頁
2024智能合約安全指南_第5頁
已閱讀5頁,還剩49頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

智能合約安全指南PAGEPAGE10目錄測試標準 6合約平臺底層安全 6合約設計與實現(xiàn)安全 8合約生態(tài)工具安全 11合約交互與數(shù)據(jù)安全 13典型案例 15重入漏洞 15整數(shù)溢出漏洞 15合約庫安全問題 16復雜DeFi協(xié)議組合安全 16非標準接口 16管理員權限過高或較中心化 17業(yè)務邏輯與區(qū)塊鏈共識產生沖突 17開發(fā)者應持續(xù)關注更多安全案例 18最佳實踐 18基本對策 18社區(qū)最佳實踐和安全開發(fā)資料 20推薦工具 214:以太坊智能合約審計CheckList 22編碼規(guī)范問題 23設計缺陷問題 26編碼安全問題 32編碼設計問題 42編碼問題隱患 495.總結 57參考資料 58測試標準理、多方游戲等各類去中心化應用(dApp)。與傳統(tǒng)程序一樣,智能合約中存在漏洞詳細具體要求,旨在通過規(guī)范測試標準提升智能合約安全水平。圖1-1下面,我們從合約平臺底層安全、合約設計與實現(xiàn)安全、合約生態(tài)工具安全、合約交互與數(shù)據(jù)安全四個方面規(guī)范智能合約安全測試標準。合約平臺底層安全本部分討論智能合約平臺相關的底層安全,包括虛擬機、區(qū)塊鏈節(jié)點等。虛擬機安全虛擬機是智能合約運行所需的環(huán)境(EVM),關系著合能在虛擬機內正確且安全地執(zhí)行。具體要求:1、不同操作符資源定價合理(如以太坊中對不同指令收取不同Gas費用)2、準確執(zhí)行操作符,操作符無二義性3、能保證合約程序的終止性4、能夠安全處理異常調用5、能保證合約對數(shù)據(jù)段的正確讀取權限6、不包含存在明顯安全隱患的特性7、不存在虛擬機逃逸漏洞8、不存在任意代碼執(zhí)行漏洞9、不存在DOS漏洞10、預編譯合約(內建合約、系統(tǒng)合約)調用與執(zhí)行安全區(qū)塊鏈節(jié)點安全常工作,且不影響節(jié)點間的同步與共識。具體要求:1、區(qū)塊鏈系統(tǒng)(節(jié)點)運營者需要做好安全防護,保障主機安全(節(jié)點)需要防止已部署合約代碼被任意篡改(節(jié)點)需要防止針對已部署合約的非法調用4、提供合約查詢和調用服務的節(jié)點需確保自身狀態(tài)正確且保持最新5、防止合約部署或調用占用節(jié)點過多計算資源6、防止合約部署或調用引起節(jié)點崩潰7、能夠正常防范針對節(jié)點發(fā)起的其他形式的攻擊合約設計與實現(xiàn)安全范標準、權限安全等。常見合約安全漏洞還尤其要關注傳統(tǒng)程序與智能合約開發(fā)之間的差異。合約代碼實現(xiàn)應規(guī)避常見安全漏洞。具體要求:正常通過編譯且無警告輸出合約各功能能夠正常執(zhí)行不存在冗余代碼能夠安全進行和接受轉賬無「數(shù)值溢出」漏洞無「隨機數(shù)預測」漏洞依賴的合約代碼庫安全可靠合理校驗輸入數(shù)據(jù)嚴格進行權限校驗不存在未知步數(shù)循環(huán)無「重入(Reentrancy)」漏洞充分檢查合約中硬編碼的地址通知事件(Event)與代碼實際執(zhí)行保持一致關鍵邏輯判斷不依賴可被礦工操縱的變量(區(qū)塊哈希、時間戳等)關鍵邏輯判斷不依賴可被用戶任意操縱的變量(如合約內資產數(shù)量等)無嚴重消耗資源(Gas、EOSRam)的操作合約正式部署上線前應該經過詳細測試(95%以上)或第三方安全審計業(yè)務邏輯安全務邏輯設計與實現(xiàn)應不存在明顯安全問題。具體要求:合約的機制設計無明顯缺陷合約代碼實現(xiàn)與業(yè)務邏輯設計相符合,不存在歧義多角色的合約,各角色權限和優(yōu)先級劃分明確多角色的合約,各角色之前的公平性有一定保障存在博弈環(huán)節(jié)的合約,博弈過程無明顯漏洞不存在「搶先交易」或「交易順序依賴」的風險不存在「拒絕服務」(Denial-of-Service,DoS)風險不會因「薅羊毛」等行為給合約正常運行造成不良影響不存在交易參數(shù)簽名重放問題業(yè)務邏輯不會與合約所屬區(qū)塊鏈系統(tǒng)共識機制產生沖突合約規(guī)范標準特定類型合約應符合相應標準。具體要求:函數(shù)及變量標明可見性,遵循變量聲明和作用域的規(guī)范變量標明數(shù)據(jù)類型不使用已過期或即將廢棄的用法代碼風格簡潔,命名規(guī)范(ERC20ERC721),保持接口和實現(xiàn)的兼容性使用經過多方充分討論和審計的合約標準和代碼實現(xiàn)使用規(guī)范使用內置函數(shù)合約權限安全限等需要劃分清晰。具體要求:限制管理員的特殊權限,防止權限過大,不可危及普通用戶安全合約管理員賬戶應該通過多重簽名錢包(合約)的方式來安全管理安全保護管理員私鑰,防止因管理員身份被盜用對合約造成致命影響DAO降低風險合約生態(tài)工具安全本部分討論智能合約生態(tài)基礎設施工具安全,包括合約語言、編譯器、測試工具、代碼掃描工具等。合約語言設計安全影響開發(fā)難度、開發(fā)效率以及合約安全性。具體要求:具備完整明確的語義定義不存在容易導致漏洞的非確定語義語言開發(fā)文檔描述完整,文檔定義與語言實現(xiàn)保持一致不包含存在安全隱患的特性編譯器安全編譯器通過語法、語義分析,將開發(fā)者編寫的源代碼經過解析最終轉換為目標代碼(通常是機器碼全檢測特性,從而可盡早幫助開發(fā)者發(fā)現(xiàn)源代碼中的常見安全風險。具體要求:能夠正確地將源代碼翻譯成目標代碼能夠檢查常見合約漏洞并給出清晰的提示編譯優(yōu)化不應引入新的代碼漏洞或改變代碼原始功能編譯器的實現(xiàn)具備完整的測試框架和測試用例測試工具、代碼掃描工具、測試環(huán)境開發(fā)者測試成本,進而提高代碼質量。具體要求:備有完善的調試工具,方便開發(fā)者部署和調用合約,追蹤狀態(tài)變化技術,供開發(fā)者快速掃描代碼,對高風險處給出警示備有公開的測試網絡,能夠準確模擬正式主網提供準確的測試用例覆蓋率分析報告支持連接正式主網,便于對已部署程序進行檢查測試合約形式化規(guī)范、形式化驗證工具采用形式化驗證工具對合約進行可靠性驗證、采用形式化語言為智能合約編寫規(guī)范。具體要求:開發(fā)者應該優(yōu)先選擇經過完整形式化證明的模版代碼高價值的智能合約應考慮引入形式化驗證技術來保障安全題定位和修改建議形式化驗證工具能夠對通過檢查的智能合約代碼自動出具帶有簽名的驗證報告,作為代碼安全性的證明文件合約交互與數(shù)據(jù)安全本部分討論智能合約與外部應用發(fā)生交互的安全性、鏈上數(shù)據(jù)安全以及鏈下“預言機”安全。外部應用與合約交互安全約,合約也需要對用戶的輸入做嚴格檢查。具體要求:外部應用或用戶可正常通過接口訪問或調用智能合約功能合約需要為外部調用者提供詳細的接口說明外部應用或用戶應使用安全的工具與合約交互合約各個函數(shù)實現(xiàn)需對傳入參數(shù)做嚴格校驗檢查合約代碼實現(xiàn)與觸發(fā)的外部事件是否一致,事件是否會非預期地觸發(fā)需評估不同種類的合約經過組合使用后的安全風險不調用未知合約對接外部合約之前需要有兼容性的安全評估和測試調用外部合約需要對其返回值等進行校驗數(shù)據(jù)上鏈安全真實業(yè)務數(shù)據(jù)上鏈需注意數(shù)據(jù)安全問題。具體要求:防范敏感或隱私數(shù)據(jù)的非預期泄漏防范敏感或隱私數(shù)據(jù)被非授權使用或訪問需解決特定應用場景下的數(shù)據(jù)可靠性和真實性問題需考慮特定數(shù)據(jù)上鏈后公開且不可更改與法律法規(guī)的合規(guī)性問題鏈下“預言機”安全(Oracle)用場景廣泛。具體要求:資產交易合約應優(yōu)先選擇去中心化預言機。應確保預言機所提供的數(shù)據(jù)不被輕易惡意操縱和篡改應確保預言機能保障數(shù)據(jù)真實性、時效性和可用性類情況下合約自身的安全典型案例析經典的智能合約安全事件案例,可以更深入地理解智能合約安全。重入漏洞2016618日,針對DAO合約的攻擊3,600,000個Ether的損失。DAO整數(shù)溢出漏洞整數(shù)溢出漏洞也是智能合約中常見的安全漏洞。例如,uint256是以太坊智能合約語言Solidity中最常用的無符號整數(shù)類型,它可表示的整數(shù)范圍為0~2256-1。當在智能合約中,整數(shù)通常與賬戶余額或者其他關鍵程序邏輯相關。利用整數(shù)溢2018年4月22日,黑客攻擊了美鏈(BEC)的Token合約,通過一個整數(shù)溢出安全漏洞,將大量Token砸向交易所,導致BEC的價格幾乎歸零。2018425爆出類似的整數(shù)溢出漏洞文數(shù)字規(guī)模的SMT價格崩盤。整數(shù)溢出漏洞可能造成以下嚴重后果:使程序偏離設計意圖實現(xiàn)無限量增發(fā)繞過權限控制或余額校驗完成一些虛假操作被管理員利用,留下作惡空間合約庫安全問題2017年11月06日,Parity多重簽名錢包漏洞導致了超過513,701個以太幣被鎖死,至今關于以太坊是否需要通過硬分叉方式升級(EIP999)爭論還在繼續(xù)。DeFi協(xié)議組合安全2020年2月15日、2月18日,DeFi項目bZx借貸協(xié)議遭遇兩次攻擊,先后損失35萬美元和64“閃電貸縱、搶先交易緊密關聯(lián),充分利用了多個DeFi產品間的可組合性來達到攻擊目的。20204192500萬美元的資產被洗劫一空,直接原因在于產品本身的可重入問題和特殊的ERC-777類imBTCUniswap去中心化交易所。非標準接口以最被廣泛接受的智能合約標準ERC20為例,很多合約未參照ERC20標準實現(xiàn),這給dApp開發(fā)帶來很大的困擾。數(shù)以千計的已部署合約曾(OpenZeppelin(52120a8c42[2017321日]~6331dd125d[2017年7月13日])給出的不規(guī)范模版代碼,多個函數(shù)實現(xiàn)沒有遵循ERC20規(guī)范,導致Solidity編譯器升級至0.4.22后出現(xiàn)嚴重的兼容性問題,無法正常轉賬。管理員權限過高或較中心化2018年7月10Bancor稱遭到攻擊1250Bancor代幣和100Pundix代Bancoranor主要合約包括Smartn和anororrE20BancorConverter合約有關,攻擊者極有可能獲取了合約管理員賬戶的私鑰,對轉換代幣合約BancorConverter擁有極高權限。owner作為該合約的所有者和管理員,有唯一的權限通過)方法提走合約中的全部ERC20至任意地址。事件發(fā)生后Bancorowner管理員的超級權限,甚至稱之為“后門。業(yè)務邏輯與區(qū)塊鏈共識產生沖突曾經紅極一時的類Fomo3D游戲,就曾因「隨機數(shù)預測」攻擊和「阻塞交易」攻擊喪失游戲公平性,淪為黑客淘金的重災地。類Fomo3D游戲的空投機制利用「隨機數(shù)」來控制中獎概率,但是由于隨機數(shù)的難度等,因此在以太坊上可以很容易的預測所謂的「隨機數(shù)」。每個區(qū)塊中僅打包很少的交易(降低他人交易被打包的可能性),類Fomo3D游戲中黑客利用了以太坊共識協(xié)議的特點,找到游戲機制的漏洞進而開發(fā)者應持續(xù)關注更多安全案例存在著各類未知風險。為了開發(fā)成熟、安全、可靠的智能合約,開發(fā)者應持續(xù)關注更多安全案例,從中學習經驗教訓。最佳實踐從智能合約開發(fā)者方面來看,區(qū)塊鏈項目的爆發(fā)吸引了大量開發(fā)者,而這些開發(fā)者往往只能從簡單的參考和修改網上良莠不齊甚至有嚴重漏洞的示例代碼開始進行學習開發(fā)。對于資深智能合約開發(fā)者而言,實現(xiàn)安全無漏洞的智能合約依舊是不小的挑戰(zhàn)。事實上,開發(fā)者可以通過遵循最佳實踐的方法,提高代碼質量,降低安全風險?;緦Σ哚槍χ悄芎霞s安全生態(tài)中常見的問題,有如下幾點基本對策:規(guī)范合約安全開發(fā)和發(fā)布流程引入形式化方法保障合約安全開發(fā)更優(yōu)秀的合約語言、編譯器和虛擬機社區(qū)應重視合約標準對策一:規(guī)范合約安全開發(fā)和發(fā)布流程測試以及必要時引入外部第三方安全審計。開發(fā)者可閱讀智能合約安全開發(fā)資料,熟悉常見安全漏洞和安全事故,并自覺規(guī)避。此外,開發(fā)者也可在開發(fā)過程中引入開源的智能合約安全自動掃描工具,從而規(guī)范合約代碼書寫,并提前發(fā)現(xiàn)常見的安全風險。這些工具通常采用靜態(tài)分析、符號執(zhí)行、污點分析、模糊測試等技術,可較快掃描出常見的漏洞和高風險代碼。對策二:引入形式化方法保障合約安全關工具。其中,形式化規(guī)范(formalspecification)通過數(shù)學語言對系統(tǒng)的預期行為(例如將數(shù)量S的token從賬戶A轉移到賬戶B)和性質(例如轉賬不會造成賬戶B額度的溢出)進行嚴格和全面的定義。形式化規(guī)范往往定義了系統(tǒng)的正確性和安全性。(refinement)(synthesis)明在內的一系列方法在數(shù)學上嚴格的保證迭代產生的系統(tǒng)和迭代前的規(guī)范或者系統(tǒng)保持一致。除了從形式化規(guī)范出發(fā)設計和實現(xiàn)一個系統(tǒng),我們也可以使用包括符號執(zhí)行(symbolicexecution)、模型檢測(modelchecking)和形式化證明(formalproving)在內的bug在設計和實現(xiàn)中具體位置和成因。開發(fā)高可信的軟件系統(tǒng)提供了有效的手段。對策三:開發(fā)更優(yōu)秀的合約語言、編譯器和虛擬機避。而事實上不少漏洞也正是因為語言設計、編譯器或虛擬機實現(xiàn)問題而產生。很多智能合約語言一方面提供高度的靈活性和強大的表達能力,另一方面又缺乏完Solidity中忽略地址對齊問題導致短地址攻擊,混淆tx.origin和msg.sender不同對策四:社區(qū)應重視合約標準對標準提案的重視程度。ERC-20、ERC-721、ERC-998、ERC-1155、ERC-1178在內的一系列合約提案或標準。提案實現(xiàn)代碼時,不能方便地快速測試正確性,從而容易引發(fā)兼容性和安全性問題。ERC-721在這方面有不錯的表現(xiàn),社區(qū)提供了基于大量測試用例的代碼驗證器?,F(xiàn)的各種預期行為,并可以與前文提到的形式化方法實現(xiàn)對接。社區(qū)最佳實踐和安全開發(fā)資料開發(fā)者可以參考和遵循以下社區(qū)最佳實踐和安全開發(fā)資料。DASP10EthereumSmartContractBestPractices(ConsenSysDiligence)EthereumSmartContractsSecurityCheckListFromKnownsec404AguidetoEOSsmartcontractsecuritybestpractices(Slowmist)SoliditySecurityBlog推薦工具智能合約安全生態(tài)已經產生了一些優(yōu)秀的代碼安全工具和分析工具,研究人員可以借助這些工具進行智能合約安全分析,開發(fā)者也可以在開發(fā)測試過程中引入這些工具,以提升代碼質量,提前發(fā)現(xiàn)安全風險。安全工具SlitherSlither是一個Solidity智能合約靜態(tài)分析框架,還提供了一個API來給研究人員通過自定義腳本審計Solidity代碼。我們可以使用Slither做到:識別能夠修改變量值的代碼隔離受特定變量值影響的條件邏輯語句查找能夠調用特定函數(shù)的其他函數(shù)項目地址:/crytic/slitherMythrilMythril是一款針對EVM字節(jié)碼進行安全審計的工具,同時支持在線合約審計。項目地址:/ConsenSys/mythrilEchidnaEchidna是一款針對EVM字節(jié)碼進行安全審計的工具,利用了模糊測試技術,同時支持和truffle集成使用。項目地址:/crytic/echidnaEthersplayEthersplay是用于BinaryNinja的插件,可以用于分析EVM字節(jié)碼,并用圖形化的方式呈現(xiàn)函數(shù)調用流程。項目地址:/crytic/ethersplay分析工具Beosin-VaaS一鍵式”95%以上,為智能合約代碼提供“軍事級的安全驗證。官網地址:/vaas/index.htmlSolgraphSolgraph調用關系。Sol2umlSol2uml用于以UML圖的形式生成智能合約函數(shù)之間的調用關系。項目地址:/naddison36/sol2uml4:以太坊智能合約審計CheckList下面是知道創(chuàng)宇404區(qū)塊鏈安全研究團隊提供的以太坊智能合約審計CheckList。404區(qū)塊鏈安全研究團隊參考并整理兼容了各大區(qū)塊鏈安全研究團隊的529能合約安全。編碼規(guī)范問題編譯器版本合約代碼中,應指定編譯器版本。建議使用最新的編譯器版本pragmasolidity^0.4.25;老版本的編譯器可能會導致各種已知的安全問題,例如/631/#44-dividenddistributorv0.4.23更新了一個編譯器漏洞,在這個版本中如果同時使用了兩種構造函數(shù),即contracta{functiona()public{...}constructor()public{...}}會忽略其中的一個構造函數(shù),該問題只影響v0.4.22v0.4.25修復了下面提到的未初始化存儲指針問題。https://etherscan.io/solcbuginfo構造函數(shù)書寫問題對應不同編譯器版本應使用正確的構造函數(shù),否則可能導致合約所有者變更在小于0.4.22版本的solidify編譯器語法要求中,合約構造函數(shù)必須和合約名字相等,名字受到大小寫影響。如:contractOwned{functionOwned()public{}0.4.22constructorfunctioncontractOwnedconstructor()public{}owner權限被竊取等更嚴重的后果。返回標準遵循ERC20規(guī)范,要求transfer、approve函數(shù)應返回bool值,需要添加返回值代碼functiontransfer(address_to,uint256_value)publicreturns(boolsuccess)而transferFrom返回結果應該和transfer返回結果一致。事件標準遵循ERC20規(guī)范,要求transfer、approve函數(shù)觸發(fā)相應的事件functionapprove(address_spender,uint256_value)publicreturns(boolsuccess){allowance[msg.sender][_spender]=_value;emitApproval(msg.sender,_spender,_value)returntrue假充值問題轉賬函數(shù)中,對余額以及轉賬金額的判斷,需要使用require函數(shù)拋出錯誤,否則會導致外部程序錯誤地判斷為轉賬成功。functiontransfer(address_to,uint256_value)returns(boolsuccess){if(balances[msg.sender]>=_value&&_value>0){balances[msg.sender]-=_value;balances[_to]+=_value;Transfer(msg.sender,_to,_value);returntrue;}else{returnfalse;}}上述代碼可能會導致假充值。正確代碼如下:functiontransfer(address_to,uint256_amount) publicreturns(boolsuccess){require(_to!=address(0));require(_amount<=balances[msg.sender]);balances[msg.sender]=balances[msg.sender].sub(_amount);balances[_to]=balances[_to].add(_amount);emitTransfer(msg.sender,_to,_amount);returntrue;}設計缺陷問題approve授權函數(shù)條件競爭approve函數(shù)中應避免條件競爭。在修改allowance前,應先修改為0,再修改為_value。gasprice順序的前后。0approve函數(shù)的用戶,該函數(shù)的操作在一定程度上是不可逆的。functionapprove(address_spender,uint256_value)publicreturns(boolsuccess){allowance[msg.sender][_spender]=_value;returntrue上述代碼就有可能導致條件競爭。應在approve中加入require((_value==0)||(allowance[msg.sender][_spender]==0));將allowance先改為0再改為對應數(shù)字,也可以使用increaseApprove和decreaseApprove函數(shù)來更改授權值Dos問題循環(huán)消耗問題在合約中,不推薦使用太多次的循環(huán)gasgas交易失敗。真實世界事件Simoleon(SIM)/646/Pandemica/index/detail/id/260/tag/2循環(huán)安全問題合約中,應盡量避免循環(huán)次數(shù)受到用戶控制,攻擊者可能會使用過大的循環(huán)來完成Dos攻擊Dos攻擊。functionDistribute(address[]_addresses,uint256[]_values)payablereturns(bool){for(uinti=0;i<_addresses.length;i++){transfer(_addresses[i],_values[i]);}returntrue;}遇到上述情況是,推薦使用withdrawFunds來讓用戶取回自己的代幣,而不是發(fā)送給對應賬戶,可以在一定程序上減少危害。上述代碼如果控制函數(shù)調用,那么就可以構造巨大循環(huán)消耗gas,造成Dos問題凍結賬戶檢測賬戶是否被凍結。freezen()合約在進行代幣轉移時只判斷了代幣來源賬戶是否處于凍結狀態(tài)而未判斷代幣的接受賬戶是否處于凍結狀態(tài),導致向凍結的賬戶轉賬后轉移的資產無法再從中轉出。下述代碼就存在凍結賬戶檢測缺陷:/*Acontractattemptstogetthecoins*/functiontransferFrom(address_from,address_to,uint256_value)returns(boolsuccess){if(frozenAccount[_from])throw;if(balanceOf[_from]<_value)throw;if(balanceOf[_to]+_value<balanceOf[_to])throw;if(_value>allowance[_from][msg.sender])throw;balanceOf[_from]-=_value;balanceOf[_to]+=_value;allowance[_from][msg.sender]-=_value;_to,_value);returntrue;}應該在校驗代幣來源賬戶是否凍結的同時校驗目標賬戶是否被凍結:/*Acontractattemptstogetthecoins*/functiontransferFrom(address_from,address_to,uint256_value)returns(boolsuccess){if(frozenAccount[_from])throw;if(frozenAccount[_to])throw;if(balanceOf[_from]<_value)throw;if(balanceOf[_to]+_value<balanceOf[_to])throw;if(_value>allowance[_from][msg.sender])throw;balanceOf[_from]-=_value;balanceOf[_to]+=_value;allowance[_from][msg.sender]-=_value;_to,_value);returntrue;}Pausable模塊繼承PausableOwnableERC20有交易```{.solidity}contractOwnable{addresspublicowner;eventOwnershipTransferred(addressindexedpreviousOwner,addressindexednewOwner);constructor()public{owner=msg.sender;}modifieronlyOwner(){require(msg.sender==owner);_;}functiontransferOwnership(address_newOwner)publiconlyOwner{_transferOwnership(_newOwner);}function_transferOwnership(address_newOwner)internal{require(_newOwner!=address(0));emitOwnershipTransferred(owner,_newOwner);owner=_newOwner;}}contractPausableisOwnable{eventPause();eventUnpause();boolpublicpaused=false;modifierwhenNotPaused(){require(!paused);_;}modifierwhenPaused(){require(paused);_;}functionpause()onlyOwnerwhenNotPausedpublic{paused=true;emitPause();}functionunpause()onlyOwnerwhenPausedpublic{paused=false;emitUnpause();}}contractStandardTokenisPausable{functiontransfer(address_to,uint256_value)publicwhenNotPausedreturns(bool){//...}function transferFrom(address _from, address _to, uint256 _value) whenNotPausedreturns(bool){//...}}編碼安全問題溢出問題算術溢出在調用加減乘除時,應使用safeMath庫來替代,否則容易導致算數(shù)上下溢,造成不可避免的損失pragmasolidity^0.4.18;contractToken{mapping(address=>uint)balances;uintpublictotalSupply;functionToken(uint_initialSupply){balances[msg.sender]=totalSupply=_initialSupply;}functiontransfer(address_to,uint_value)publicreturns(bool){require(balances[msg.sender]-_value>=0);//可以通過下溢來繞過判斷balances[msg.sender]-=_value;balances[_to]+=_value;returntrue;}functionbalanceOf(address_owner)publicconstantreturns(uintbalance){returnbalances[_owner];}}balances[msg.sender]-_value>=0可以通過下溢來繞過判斷。正確的寫法如下:functiontransfer(address_to,uint256_amount) publicreturns(boolsuccess){require(_to!=address(0));require(_amount<=balances[msg.sender]);balances[msg.sender]=balances[msg.sender].sub(_amount);balances[_to]=balances[_to].add(_amount);emitTransfer(msg.sender,_to,_amount);returntrue;}真實世界事件Hexagon代幣變泡沫,以太坊Hexagon溢出漏洞比狗莊還過分SMT/BECSolidity合約中的整數(shù)安全問題——SMT/BEC合約整數(shù)溢出解析鑄幣燒幣溢出問題鑄幣函數(shù)中,應對totalSupply設置上限,避免因為算術溢出等漏洞導致惡意鑄幣增發(fā)functionTokenERC20(uint256initialSupply,stringtokenName,stringtokenSymbol)public{totalSupply=initialSupply*10**uint256(decimals);balanceOf[msg.sender]=totalSupply;name=tokenName;symbol=tokenSymbol;}上述代碼中就未對totalSupply做限制,可能導致指數(shù)算數(shù)上溢。正確寫法如下:contractOPL{//Publicvariablesstringpublicname;stringpublicsymbol;uint8publicdecimals=18;//18decimalsboolpublicadminVer=false;addresspublicowner;uint256publictotalSupply;functionOPL()public{totalSupply=210000000*10**uint256(decimals);...}重入漏洞智能合約中避免使用call來交易,避免重入漏洞callsendtransfercall最大的gasgasoutof。重入漏洞有幾大特征。1、使用了call函數(shù)作為轉賬函數(shù)2、沒有限制call函數(shù)的gas3、扣余額在轉賬之后4、call時加入了()來執(zhí)行fallback函數(shù)functionwithdraw(uint_amount){require(balances[msg.sender]>=_amount);msg.sender.call.value(_amount)();balances[msg.sender]-=_amount;}上述代碼就是一個簡單的重入漏洞的demo。通過重入注入轉賬,將大量合約代幣遞歸轉賬而出。transfercall執(zhí)gas,都可以有效的減少該問題的危害。contractEtherStore{//initialisethemutexboolreEntrancyMutex=false;uint256publicwithdrawalLimit=1ether;mapping(address=>uint256)publiclastWithdrawTime;mapping(address=>uint256)publicbalances;functiondepositFunds()publicpayable{balances[msg.sender]+=msg.value;}functionwithdrawFunds(uint256_weiToWithdraw)public{require(!reEntrancyMutex);require(balances[msg.sender]>=_weiToWithdraw);//limitthewithdrawalrequire(_weiToWithdraw<=withdrawalLimit);//limitthetimeallowedtowithdrawrequire(now>=lastWithdrawTime[msg.sender]+1weeks);balances[msg.sender]-=lastWithdrawTime[msg.sender]=now;//setthereEntrancymutexbeforetheexternalcallreEntrancyMutex=true;//releasethemutexaftertheexternalcallreEntrancyMutex=false;}}上述代碼是一種用互斥鎖來避免遞歸防護方式。真實事件:TheDaoTheDAOTheDAOaddresscall注入call函數(shù)調用時,應該做嚴格的權限控制,或直接寫死call調用的函數(shù)在EVM的設計中,如果call的參數(shù)data是0xdeadbeef(假設的一個函數(shù)名)+0x0000000000 01,這樣的話就是調用函數(shù)call注入可能導致代幣竊取,權限繞過,通過call注入可以調用私有函數(shù),甚至部分高權限函數(shù)。addr.call(data);addr.delegatecall(data);addr.callcode(data);存環(huán)境的復雜性,防止攻擊者通過修改狀態(tài)來攻擊合約。真實世界事件call注入以太坊智能合約call注入攻擊以太坊Solidity合約call函數(shù)簇濫用導致的安全風險權限控制合約中不同函數(shù)應設置合理的權限publicprivatemodifierfunctioninitContract()public{owner=msg.sender;}上述代碼作為初始函數(shù)不應該為public。真實世界事件:ParityMulti-sigbug1ParityMulti-sigbug2Rubixi重放攻擊合約中如果涉及委托管理的需求,應注意驗證的不可復用性,避免重放攻擊付一定的費用給受托人。這個業(yè)務場景在智能合約中也比較普遍。transferProxyuser1tokenuser3,但沒有ethgaspriceuser2transferProxy來完成。functiontransferProxy(address_from,address_to,uint256_value,uint256_fee,uint8_v,bytes32_r,bytes32_s)publicreturns(bool){if(balances[_from]<_fee+_value||_fee>_fee+_value)revert();uint256nonce=nonces[_from];bytes32h=keccak256(_from,_to,_value,_fee,nonce,address(this));if(_from!=ecrecover(h,_v,_r,_s))revert();if(balances[_to]+_value<balances[_to]||balances[msg.sender]+_fee<balances[msg.sender])revert();balances[_to]+=_value;emitTransfer(_from,_to,_value);balances[msg.sender]+=_fee;emitTransfer(_from,msg.sender,_fee);balances[_from]-=_value+_fee;nonces[_from]=nonce+1;returntrue;}nonce放攻擊,多次轉賬。漏洞來自于Defcon2018演講議題ReplayAttacksonEthereumSmartContractsReplayAttacksonEthereumSmartContractspdf一致性檢查智能合約中更新邏輯(例如:代幣數(shù)量更新、授權轉賬額度更新等)時往往都會伴隨著對操作對象的檢查邏輯(例如:防溢出檢查、授權轉賬額度檢查等之外的邏輯,這里以合約中的授權轉賬函數(shù)為例做簡要說明:functiontransferFrom(address_from,address_to,uint256_value)returns(boolsuccess)allowance[_from][msg.sender]///@noticesend`_value`tokento`_to`from`_from`ontheconditionitisapprovedby`_from`///@param_fromTheaddressofthesender///@param_toTheaddressoftherecipient///@param_valueTheamountoftokentobetransferred///@returnWhetherthetransferwassuccessfulornotfunctiontransferFrom(address_from,address_to,uint256_value)publicreturns(boolsuccess){require(balances[_from]>=_value); //Checkifthesenderhasenoughrequire(balances[_to]+_value>=balances[_to]);//Checkforoverflowsrequire(_value<=allowed[_from][msg.sender]); //Checkallowancebalances[_from]-=_value;balances[_to]+=_value;allowed[_from][_to]-=_value;_to,_value);returntrue;}在上述代碼中下面這一行代碼用于檢測授權轉賬的額度:require(_value<=allowed[_from][msg.sender]);而下面這一行代碼用于更新授權轉賬的額度:allowed[_from][_to]-=_value;權限后,被授權轉賬的用戶能夠持續(xù)轉賬,直到轉完授權賬戶的所有余額為止。正確的寫法應該是保持判斷邏輯與更新邏輯中的操作對象一致且為被授權轉賬用戶的授權轉賬額度:require(_value<=allowed[_from][msg.sender]);allowed[_from][msg.sender]);-=_value;編碼設計問題地址初始化問題涉及到地址的函數(shù)中,建議加入require(_to!=address(0))驗證,有效避免用戶誤操作或未知錯誤導致的不必要的損失EVM0,如果開發(fā)者在代碼中初始化了某addressaddress變量值,但在下面的代碼中操作了這個變量,就可能導致不必要的安全風險。這樣的檢查可以以最簡單的方式避免未知錯誤、短地址攻擊等問題的發(fā)生。判斷函數(shù)問題requireassertassert會導致剩gas全部消耗掉,而他們在其他方面的表現(xiàn)都是一致的值得注意的是,assert存在強制一致性,對于固定變量的檢查來說,assert可以用于避免一些未知的問題,因為他會強制終止合約并使其無效化,在一些固定條件下,assert更適用。余額判斷問題不要假設合約創(chuàng)建時余額為0,可以強制轉賬謹慎編寫用于檢查賬戶余額的不變量,因為攻擊者可以強制發(fā)送wei到任何賬戶,即使fallback函數(shù)throw也不行。1weiselfdestruct(victimAddress)0.轉賬函數(shù)問題在完成交易時,默認情況下推薦使用transfer而不是send完成交易當transfer或者send函數(shù)的目標是合約時,會調用合約的fallback函數(shù),但fallback函數(shù)執(zhí)行失敗時。transfersendfalsesend時需要判斷返回類型,否則可能會導致轉賬失敗但余額減少的情況。functionwithdraw(uint256_amount)public{require(balances[msg.sender]>=_amount);balances[msg.sender]-=_amount;etherLeft-=_amount;msg.sender.send(_amount);}send()函數(shù)進行轉賬,因為這里沒有驗證send()msg.sender為合約賬戶fallback()send()代碼外部調用設計問題對于外部合約優(yōu)先使用pull而不是push的把對外的操作改為用戶自己來取。錯誤樣例:contractauction{addresshighestBidder;uinthighestBid;functionbid()payable{if(msg.value<highestBid)throw;if(highestBidder!=0){if(!highestBidder.send(highestBid)){//可能會發(fā)生錯誤throw;}}highestBidder=msg.sender;highestBid=msg.value;}}當需要向某一方轉賬時,將轉賬改為定義withdraw函數(shù),讓用戶自己來執(zhí)行合約將余額取出,這樣可以最大程度的避免未知的損失。范例代碼:contractauction{addresshighestBidder;uinthighestBid;mapping(address=>uint)refunds;functionbid()payableexternal{if(msg.value<highestBid)throw;if(highestBidder!=0){refunds[highestBidder]+=highestBid;//記錄在refunds中}highestBidder=msg.sender;highestBid=msg.value;}functionwithdrawRefund()external{uintrefund=refunds[msg.sender];refunds[msg.sender]=0;if(!msg.sender.send(refund)){refunds[msg.sender]=refund;//如果轉賬錯誤還可以挽回}}}錯誤處理合約中涉及到call等在address底層操作的方法時,做好合理的錯誤處理address.call()address.callcode()address.delegatecall()address.send()這類操作如果遇到錯誤并不會拋出異常,而是會返回false并繼續(xù)執(zhí)行。functionwithdraw(uint256_amount)public{require(balances[msg.sender]>=_amount);balances[msg.sender]-=_amount;etherLeft-=_amount;msg.sender.send(_amount);}sendmsg.sender調用失敗時,sendfalse。所以當使用上述方法時,需要對返回值做檢查并做錯誤處理。if(!someAddress.send(55)){//Somefailurecode}/607/#4-unchecked-return-values-for-low-level-calls值得注意的一點是,作為EVM設計的一部分,下面這些函數(shù)如果調用的合約不存在,將會返回Truecall、delegatecall、callcode、staticcall在調用這類函數(shù)之前,需要對地址的有效性做檢查。弱隨機數(shù)問題智能合約上隨機數(shù)生成方式需要更多考量Fomo3D合約在空投獎勵的隨機數(shù)生成中就引入了block信息作為隨機數(shù)種子生成的參數(shù),導致隨機數(shù)種子只受到合約地址影響,無法做到完全隨機。functionairdrop()privateviewreturns(bool){uint256seed=uint256(keccak256(abi.encodePacked((block.timestamp).add(block.difficulty).add((uint256(keccak256(abi.encodePacked(block.coinbase))))/(now)).add(block.gaslimit).add((uint256(keccak256(abi.encodePacked(msg.sender))))/(now)).add(block.number))));if((seed-((seed/1000)*1000))<airDropTracker_)return(true);elsereturn(false);}上述這段代碼直接導致了Fomo3d薅羊毛事件的誕生。真實世界損失巨大,超過數(shù)千eth。要。hash-commit-reveal,即玩家提交行動計hashhashcommit有一個很棒的實現(xiàn)代碼是dice2win的隨機數(shù)生成代碼。hash-commit-reveal最大的問題在于服務端會在用戶提交之后短暫的獲得整個過安全分析當然hash-commit在一些簡單場景下也是不錯的實現(xiàn)方式。即玩家提交行動計劃的hash,然后生成隨機數(shù),然后提交行動計劃。真實世界事件:Fomo3d薅羊毛/r/ethereum/comments/916xni/how_to_pwn_fomo3d_a_beginners_guide/8萬筆交易「封死」以太坊網絡,只為搶奪Fomo3D大獎?LastWinner/672/變量覆蓋問題在合約中避免array變量key可以被控制map[uint256(msg.sender)+x]=blockNum;在EVM中數(shù)組和其他類型不同,因為數(shù)組時動態(tài)大小的,所以數(shù)組類型的數(shù)據(jù)計算方式為address(map_data)=sha3(key)+offset其中key就是map變量定義的位置,也就是1,offset就是數(shù)組中的偏移,比如map[2],offset就是2.map[2]的地址就是sha3(1)+2,假設map[2]=2333,則storage[sha3(1)+2]=2333。這樣一來就出現(xiàn)問題了,由于offset我們可控,我們就可以向storage的任意地址寫值。這就可能覆蓋storage的任意地址的值,影響代碼本身的邏輯,導致進一步更嚴重的問題。詳細的原理可以看OPCODE逆向之理論基礎篇/739/編碼問題隱患語法特性問題在智能合約中小心整數(shù)除法的向下取整問題精度時,我們需要使用乘數(shù)來加大這個數(shù)字。式出現(xiàn),將會采取向下取整的處理方式。錯誤樣例uintx=5/2;//2正確代碼uintmultiplier=10;uintx=(5*multiplier)/2;數(shù)據(jù)私密問題注意鏈上的所有數(shù)據(jù)都是公開的存在鏈上。數(shù)據(jù)可靠性合約中不應該讓時間戳參與到代碼中,容易受到礦工的干擾,應使用block.height等不變的數(shù)據(jù)uintsomeVariable=now+1;if(now%2==0){//now可能被礦工控制}gas消耗優(yōu)化對于某些不涉及狀態(tài)變化的函數(shù)和變量可以加constant來避免gas的消耗contractEUXLinkTokenisERC20{usingSafeMathforuint256;addressowner=msg.sender;mapping(address=>uint256)balances;mapping(address=>mapping(address=>uint256))allowed;mapping(address=>bool)publicblacklist;stringpublicconstantname="xx";stringpublicconstantsymbol="xxx";uintpublicconstantdecimals=8;uint256publictotalSupply=1000000000e8;uint256publictotalDistributed=200000000e8;uint256publictotalPurchase=200000000e8;uint256 public totalRemaining totalSupply.sub(totalDistributed).sub(totalPurchase);uint256publicvalue=5000e8;uint256publicpurchaseCardinal=5000000e8;uint256publicminPurchase=0.001e18;uint256publicmaxPurchase=10e18;合約用戶合約中,應盡量考慮交易目標為合約時的情況,避免因此產生的各種惡意利用contractAuction{addresspubliccurrentLeader;uint256publichidghestBid;functionbid()publicpayable{require(msg.value>highestBid);require(currentLeader.send(hi

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論