這張圖總結了智能合約攻防的各個方面,分爲兩大部分:鏈上攻防和鏈下攻防。
鏈上攻防
對於鏈上攻防,選取5個方面進行詳細剖析。“Talk is cheap. Show me the code.” 下面開始展示源碼詳細解讀。
1、溢出
因爲沒有使用 SaceMath導致的溢出造成金額可以在攻擊者構造的數據中被任意控制,從而導致條件判斷成立最終攻擊者成功攻擊了該智能合約。
此類問題避免方法:在做數字計算的相關代碼處嚴格使用 SafeMath 進行做算術運算,防止溢出產生。
2、權限控制
此類權限控制屬於權限問題中的一種,由於合約開發人員的不專業導致的代理轉賬函數中未進行授權判斷,從而導致任何人都可以轉走別人的錢。
此類問題避免方法:找專業的開發人員進行合約開發,同時合約開發人員也需要及時提高自己的開發技能以及專業能力,由於區塊鏈的去中心和Token的特殊性很容易因爲一個小的漏洞導致項目方從此身敗名裂,所以建議項目上線之前找專業的安全審計團隊出職業的審計報告,此類問題慢霧在審計的過程中一定能夠發現並幫助項目方避免這種尷尬的情況。
3、條件競爭
此類問題爲條件競爭導致的跨合約調用多次惡意轉賬最終攻擊者成功完成攻擊,在跨合約調用方面一直存在很大的問題,由於開發者的專業技能不夠專業或者是合約設計的不合理等問題都會導致此類條件競爭。
此類問題避免方法:瞭解 Solidity 語言本身的函數使用場景以及底層實現,並且在業務邏輯上先扣除需要減去的賬戶金額再添加到對應賬戶並及時清零中間變量的臨時值,避免因爲事務的問題導致多入賬導致損失,同時也要了解清楚Solidity中someAddress.call.value()方法 和 someAddress.transfer() 方法還有someAddress.send() 方法的區別。非常不建議使用 someAddress.call.value(),具體原因請大家一定要自己動手去查一下。
4、假充值
假充值這類問題是一個系列,比如XRP的假充值和USDT的假充值等,在這裏只講 ERC20 的假充值問題,這種問題是由於合約開發人員的邏輯判斷代碼不規範導致在以太坊的區塊瀏覽器中可以看到原本一筆失敗的轉賬狀態爲Success交易所在進行充值入賬的時候也沒有嚴格進行狀態的判斷和金額的校驗從而導致了此類問題,此類問題殺傷力強,並且一次足以讓一個交易所虧損上百萬及千萬,無論是開發者還是交易所都需要足夠重視假充值的問題。
此類問題避免方法:合約開發中代碼判斷邏輯的地方使用 require()或者 assert()進行判斷,如果條件不滿足會直接導致 transfer 的失敗同時狀態也是 fail ,交易所錢包業務開發人員需要在充值的時候注意嚴格校驗轉賬狀態是成功還是失敗,同時對充值金額也進行校驗,確認真的到賬後此筆交易纔算成功。
5、惡意事件
此處需要說明這裏的代碼僅僅是我們編寫作爲演示使用的,目前暫時沒有發現這種真是的攻擊行爲。由於區塊鏈的數據都記錄在鏈上,惡意記錄 event 的事件可以直接修改對應的參數,如果此代碼真是存在則上面講的假充值就真的成了真充值了。
此類問題避免方法:做好 Code Review 和找專業的代碼審計。
鏈下攻防
對於鏈下攻防,同樣從5個方面詳細剖析。
1、WEB
在之前發生的MyEtherWallet發生的域名劫持事件前,實際上相關的安全機構已經給出了中級風險的提示(如下圖),但是它並沒有對此重視。
專題介紹:https://mp.weixin.qq.com/s/-Yiul1QtSNa9JJAOuXCx3A
2、終端
對於硬件錢包的安全,也可能存在各種的安全隱患如:是否使用加密芯片,工業設計是否安全,做工是否很簡陋不考慮丟失以及意外破損和能否以應對低溫高溫的情況下正常使用,最終需要考慮你到底是買了個硬件還是真正的硬件錢包?
3、節點
比如以太坊黑色情人節事件,攻擊者通過在網絡上通過 P2P協議發現新的以太坊全節點,然後構造好攻擊腳本做好工程化等待時機對新搭建的全節點進行攻擊,由於很多小白用戶不懂得如何防禦此類攻擊所以到目前位置還是有很多團隊不斷被盜ETH。
詳見慢霧的專題頁:https://4294967296.io/eth214/
專題介紹:https://mp.weixin.qq.com/s/-Yiul1QtSNa9JJAOuXCx3A
慢霧的防禦方法:https://mp.weixin.qq.com/s/Kk2lsoQ1679Gda56Ec-zJg
4、礦工
礦工有可能作惡,針對Dapp,進行選擇性的打包。針對礦池,針對塊代扣攻擊等。
5、後端
後端的攻擊舉例爲,USDT 假充值。
攻擊步驟爲:
1)向交易所錢包構造併發起⽆效(虛假)轉賬交易;
2)由於邏輯判斷缺陷交易所將⽆效交易⼊賬並計⼊到⽤戶在交易所的資⾦賬戶;
3)⽤戶發起提幣;
4)交易所處理⽤戶提幣將幣打到⽤戶⾃⼰錢包地址;⽤戶⾃⼰充幣環節USDT 沒有任何損失,提幣環節交易所把⾃⼰真實的 USDT 幣打給⽤戶,造成交易所損失。
這背後的原理是:
USDT 是基於 Bitcoin 區塊的 OMNI 協議資產類型,利⽤ Bitcoin 的 OP_RETURN 承載相關交易數據;
Bitcoin 本⾝並不會校驗 OP_RETURN 數據的“合法性”,可以是任意數據;
Bitcoin 交易當區塊確認數達到 6 的時候,就會被Bitcoin 節點承認;
那麼,USDT 的交易在 OMNI 的節點上如何被確認的呢?
int64_t nBalance =
getMPbalance(sender,property, BALANCE);
說明:通過上⾯的代碼來看,OMNI內部有⾃⼰的⼀套基於地址的記賬模型,通過地址可以獲取地址的餘額。
因此,爲了避免以上問題。⼀筆 USDT 的交易合法的,要⾄少滿⾜以下兩個條件:
(1)要通過⽐特幣的交易來構造,要符合⽐特幣的餘額驗證(BTC)及交易規則驗證
(2)要通過 USDT ⾃⼰的餘額(USDT)驗證
對於USDT 假充值,當餘額不足時會發生什麼?
{"amount": "28.59995822",
"block": 502358,
"blockhash": "0000000000000000005968fa48c49d7c4fb2363369d59db82897853fd937c71a",
"blocktime": 1514985094,
"confirmations": 37854,
"divisible": true,
"fee": "0.00200000",
"flags": null,
"invalidreason": "Sender has insufficient balance",
"ismine": false,
"positioninblock": 301,
"propertyid": 31,
"propertyname": "TetherUS",
"referenceaddress": "1Po1oWkD2LmodfkBYiAktwh76vkF93LKnh",
"sendingaddress": "18DmsHjHU6YM2ckFzub4pBneD8QXCXRTLR",
"txid": "1b5c80f487d2bf8b69e1bbba2b1979aacb1aca7a094c00bcb9abd85f9af738ea",
"type": "Simple Send",
"type_int": 0,
"valid": false,
"version": 0 }
另外,有些黑客非常有心機,在一些百度回答等貼上錯誤的代碼,如果程序員不仔細檢查或者對智能合約不熟悉,直接複製粘貼代碼,則容易遭受攻擊。要如何提升安全呢?首先開發人員擁有安全開發意識,其次還是要找專業的安全團隊做專業的審計。