【慢霧】EOS智能合約最佳安全開(kāi)發(fā)指南區(qū)塊鏈
本文由慢霧(SlowMist)同學(xué)主導(dǎo),麒麟小組以及其他同學(xué)參與,旨在為EOS智能合約開(kāi)發(fā)人員提供一些智能合約的安全準(zhǔn)則及已知漏洞分析。https://github.com/slowmist/eos-smart-contract-security-best-practices
目錄
安全準(zhǔn)則
已知漏洞
1. 數(shù)值溢出
1.1 漏洞示例
1.2 防御方法
1.3 真實(shí)案例
2. 權(quán)限校驗(yàn)
2.1 漏洞示例
2.2 防御方法
2.3 真實(shí)案例
3. apply 校驗(yàn)
3.1 漏洞示例
3.2 防御方法
3.3 真實(shí)案例
參考文獻(xiàn)
致謝
安全準(zhǔn)則
EOS 處于早期階段并且有很強(qiáng)的實(shí)驗(yàn)性質(zhì)。因此,隨著新的 bug 和安全漏洞被發(fā)現(xiàn),新的功能不斷被開(kāi)發(fā)出來(lái),其面臨的安全威脅也是不斷變化的。這篇文章對(duì)于開(kāi)發(fā)人員編寫(xiě)安全的智能合約來(lái)說(shuō)只是個(gè)開(kāi)始。
開(kāi)發(fā)智能合約需要一個(gè)全新的工程思維,它不同于我們以往項(xiàng)目的開(kāi)發(fā)。因?yàn)樗稿e(cuò)的代價(jià)是巨大的,很難像中心化類(lèi)型的軟件那樣,打上補(bǔ)丁就可以彌補(bǔ)損失。就像直接給硬件編程或金融服務(wù)類(lèi)軟件開(kāi)發(fā),相比于 Web 開(kāi)發(fā)和移動(dòng)開(kāi)發(fā)都有更大的挑戰(zhàn)。因此,僅僅防范已知的漏洞是不夠的,還需要學(xué)習(xí)新的開(kāi)發(fā)理念:
對(duì)可能的錯(cuò)誤有所準(zhǔn)備
任何有意義的智能合約或多或少都存在錯(cuò)誤,因此你的代碼必須能夠正確的處理出現(xiàn)的 bug 和漏洞。需始終保證以下規(guī)則:
1.當(dāng)智能合約出現(xiàn)錯(cuò)誤時(shí),停止合約;
2.管理賬戶(hù)的資金風(fēng)險(xiǎn),如限制(轉(zhuǎn)賬)速率、最大(轉(zhuǎn)賬)額度;
3.有效的途徑來(lái)進(jìn)行 bug 修復(fù)和功能提升。
謹(jǐn)慎發(fā)布智能合約
盡量在正式發(fā)布智能合約之前發(fā)現(xiàn)并修復(fù)可能的 bug。
1.對(duì)智能合約進(jìn)行徹底的測(cè)試,并在任何新的攻擊手法被發(fā)現(xiàn)后及時(shí)的測(cè)試(包括已經(jīng)發(fā)布的合約)
2.從 alpha 版本在麒麟測(cè)試網(wǎng)(CryptoKylin-Testnet)上發(fā)布開(kāi)始便邀請(qǐng)專(zhuān)業(yè)安全審計(jì)機(jī)構(gòu)進(jìn)行審計(jì),并提供漏洞賞金計(jì)劃(Bug Bounty)
3.階段性發(fā)布,每個(gè)階段都提供足夠的測(cè)試
保持智能合約的簡(jiǎn)潔
復(fù)雜會(huì)增加出錯(cuò)的風(fēng)險(xiǎn)。
1.確保智能合約邏輯簡(jiǎn)潔;
2.確保合約和函數(shù)模塊化;
3.使用已經(jīng)被廣泛使用的合約或工具(比如,不要自己寫(xiě)一個(gè)隨機(jī)數(shù)生成器);
4.條件允許的話,清晰明了比性能更重要;
5.只在你系統(tǒng)的去中心化部分使用區(qū)塊鏈。
保持更新
通過(guò)公開(kāi)資源來(lái)確保獲取到最新的安全進(jìn)展。
1.在任何新的漏洞被發(fā)現(xiàn)時(shí)檢查你的智能合約;
2.盡可能快的將使用到的庫(kù)或者工具更新到最新;
3.使用最新的安全技術(shù)。
清楚區(qū)塊鏈的特性
盡管你先前所擁有的編程經(jīng)驗(yàn)同樣適用于智能合約開(kāi)發(fā),但這里仍然有些陷阱你需要留意:
require_recipient(account_name name) 可觸發(fā)通知,調(diào)用name合約中的同名函數(shù),官方文檔
https://developers.eos.io/eosio-cpp/v1.2.0/reference#section-require_recipient
已知漏洞
數(shù)值溢出
在進(jìn)行算術(shù)運(yùn)算時(shí),未進(jìn)行邊界檢查可能導(dǎo)致數(shù)值上下溢,引起智能合約用戶(hù)資產(chǎn)受損。
1.漏洞示例
存在缺陷的代碼:batchTransfer 批量轉(zhuǎn)賬
typedef struct acnts {account_name name0;account_name name1;account_name name2;account_name name3;} account_names;void batchtransfer(symbol_name symbol, account_name from, account_names to, uint64_t balance){ require_auth(from);account fromaccount; require_recipient(from); require_recipient(to.name0); require_recipient(to.name1); require_recipient(to.name2); require_recipient(to.name3); eosio_assert(is_balance_within_range(balance), "invalid balance"); eosio_assert(balance > 0, "must transfer positive balance"); uint64_t amount = balance * 4; //乘法溢出int itr = db_find_i64(_self, symbol, N(table), from); eosio_assert(itr >= 0, "Sub-- wrong name"); db_get_i64(itr, &fromaccount, (account)); eosio_assert(fromaccount.balance >= amount, "overdrawn balance"); sub_balance(symbol, from, amount); add_balance(symbol, to.name0, balance); add_balance(symbol, to.name1, balance); add_balance(symbol, to.name2, balance); add_balance(symbol, to.name3, balance);}
2.防御方法
盡可能使用 asset 結(jié)構(gòu)體進(jìn)行運(yùn)算,而不是把 balance 提取出來(lái)進(jìn)行運(yùn)算。
3.真實(shí)案例
【EOS Fomo3D你千萬(wàn)別玩】狼人殺遭到溢出攻擊, 已經(jīng)涼涼(https://bihu.com/article/995093)
權(quán)限校驗(yàn)
在進(jìn)行相關(guān)操作時(shí),應(yīng)嚴(yán)格判斷函數(shù)入?yún)⒑蛯?shí)際調(diào)用者是否一致,使用require_auth進(jìn)行校驗(yàn)。
1.漏洞示例
存在缺陷的代碼:transfer 轉(zhuǎn)賬
void token::transfer( account_name from,account_name to,asset quantity,string memo ){ eosio_assert( from != to, "cannot transfer to self" ); eosio_assert( is_account( to ), "to account does not exist"); auto sym = quantity.symbol.name();stats statstable( _self, sym ); const auto& st = statstable.get( sym ); require_recipient( from ); require_recipient( to ); eosio_assert( quantity.is_valid(), "invalid quantity" ); eosio_assert( quantity.amount > 0, "must transfer positive quantity" ); eosio_assert( quantity.symbol == st.supply.symbol, "symbol precision mismatch" ); eosio_assert( memo.size() <= 256, "memo has more than 256 bytes" ); auto payer = has_auth( to ) ? to : from; sub_balance( from, quantity ); add_balance( to, quantity, payer );}
2.防御方法
使用require_auth( from )校驗(yàn)資產(chǎn)轉(zhuǎn)出賬戶(hù)與調(diào)用賬戶(hù)是否一致。
3.真實(shí)案例
暫無(wú)
apply 校驗(yàn)
在處理合約調(diào)用時(shí),應(yīng)確保每個(gè) action 與 code 均滿足關(guān)聯(lián)要求。
1.漏洞示例
存在缺陷的代碼:
// extend from EOSIO_ABI#define EOSIO_ABI_EX( TYPE, MEMBERS ) \extern "C" { \ void apply( uint64_t receiver, uint64_t code, uint64_t action ) { \ auto self = receiver; \ if( action == N(onerror)) { \ /* onerror is only valid if it is for the "eosio" code account and authorized by "eosio"'s "active permission */ \ eosio_assert(code == N(eosio), "onerror action's are only valid from the \"eosio\" system account"); \} \ if( code == self || code == N(eosio.token) || action == N(onerror) ) { \TYPE thiscontract( self ); \ switch( action ) { \ EOSIO_API( TYPE, MEMBERS ) \} \ /* does not allow destructor of thiscontract to run: eosio_exit(0); */ \} \} \}EOSIO_ABI_EX(eosio::charity, (hi)(transfer))
2.防御方法
使用
if( ((code == self && action != N(transfer) ) || (code == N(eosio.token) && action == N(transfer)) || action == N(onerror)) ) { }
綁定每個(gè)關(guān)鍵 action 與 code 是否滿足要求,避免異常調(diào)用。
3.真實(shí)案例
EOSBet 黑客攻擊事件復(fù)盤(pán)
(https://medium.com/@eosbetcasino/eosbet-黑客攻擊事件復(fù)盤(pán)-13663d8f3f1)
參考文獻(xiàn)
保管好私鑰就安全了嗎?注意隱藏在EOS DAPP中的安全隱患
https://zhuanlan.zhihu.com/p/40625180
漏洞詳解|惡意 EOS 合約存在吞噬用戶(hù) RAM 的安全風(fēng)
https://zhuanlan.zhihu.com/p/40469719
How EOSBET attacked by aabbccddeefg
https://www.reddit.com/r/eos/comments/9fpcik/how_eosbet_attacked_by_aabbccddeefg/
BET被黑客攻擊始末,實(shí)錘還原作案現(xiàn)場(chǎng)和攻擊手段
https://github.com/ganjingcun/bet-death-causes/blob/master/README.md
累計(jì)薅走數(shù)百萬(wàn),EOS Dapps已成黑客提款機(jī)?
https://mp.weixin.qq.com/s/74ggygC3nbDihLkobXOW2w
致謝
麒麟工作組
eosiofans
荊凱(EOS42)
星魂
島娘
趙余(EOSLaoMao)
字符
1.TMT觀察網(wǎng)遵循行業(yè)規(guī)范,任何轉(zhuǎn)載的稿件都會(huì)明確標(biāo)注作者和來(lái)源;
2.TMT觀察網(wǎng)的原創(chuàng)文章,請(qǐng)轉(zhuǎn)載時(shí)務(wù)必注明文章作者和"來(lái)源:TMT觀察網(wǎng)",不尊重原創(chuàng)的行為T(mén)MT觀察網(wǎng)或?qū)⒆肪控?zé)任;
3.作者投稿可能會(huì)經(jīng)TMT觀察網(wǎng)編輯修改或補(bǔ)充。