内容简介:原文:这篇文档旨在为 EOS 智能合约开发人员提供一些智能合约的EOS 处于早期阶段并且有很强的实验性质。因此,随着新的 bug 和安全漏洞被发现,新的功能不断被开发出来,其面临的安全威胁也是不断变化的。这篇文章对于开发人员编写安全的智能合约来说只是个开始。
原文: https://github.com/slowmist/eos-smart-contract-security-best-practices
这篇文档旨在为 EOS 智能合约开发人员提供一些智能合约的 安全准则 及 已知漏洞分析 。我们邀请社区对该文档提出修改或完善建议,欢迎各种合并请求(Pull Request)。若有相关的文章或博客的发表,也请将其加入到中。
目录
安全准则
EOS 处于早期阶段并且有很强的实验性质。因此,随着新的 bug 和安全漏洞被发现,新的功能不断被开发出来,其面临的安全威胁也是不断变化的。这篇文章对于开发人员编写安全的智能合约来说只是个开始。
开发智能合约需要一个全新的工程思维,它不同于我们以往项目的开发。因为它犯错的代价是巨大的,很难像中心化类型的软件那样,打上补丁就可以弥补损失。就像直接给硬件编程或金融服务类软件开发,相比于 Web 开发和移动开发都有更大的挑战。因此,仅仅防范已知的漏洞是不够的,还需要学习新的开发理念:
-
对可能的错误有所准备
。任何有意义的智能合约或多或少都存在错误,因此你的代码必须能够正确的处理出现的 bug 和漏洞。需始终保证以下规则:
- 当智能合约出现错误时,停止合约
- 管理账户的资金风险,如限制(转账)速率、最大(转账)额度
- 有效的途径来进行 bug 修复和功能提升
-
谨慎发布智能合约
。 尽量在正式发布智能合约之前发现并修复可能的 bug。
- 对智能合约进行彻底的测试,并在任何新的攻击手法被发现后及时的测试(包括已经发布的合约)
- 从 alpha 版本在麒麟测试网(CryptoKylin-Testnet)上发布开始便邀请专业安全审计机构进行审计,并提供漏洞赏金计划(Bug Bounty)
- 阶段性发布,每个阶段都提供足够的测试
-
保持智能合约的简洁
。复杂会增加出错的风险。
- 确保智能合约逻辑简洁
- 确保合约和函数模块化
- 使用已经被广泛使用的合约或工具(比如,不要自己写一个随机数生成器)
- 条件允许的话,清晰明了比性能更重要
- 只在你系统的去中心化部分使用区块链
-
保持更新
。通过公开资源来确保获取到最新的安全进展。
- 在任何新的漏洞被发现时检查你的智能合约
- 尽可能快的将使用到的库或者 工具 更新到最新
- 使用最新的安全技术
-
清楚区块链的特性
。尽管你先前所拥有的编程经验同样适用于智能合约开发,但这里仍然有些陷阱你需要留意:
-
require_recipient(account_name name)
可触发通知,调用name
合约中的同名函数, 官方文档
-
已知漏洞
数值溢出
在进行算术运算时,未进行边界检查可能导致数值上下溢,引起智能合约用户资产受损。
漏洞示例
存在缺陷的代码: batchTransfer
批量转账
typedef struct acnts { account_name name0; account_name name1; account_name name2; account_name name3; } account_names; void transfer(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); }
防御方法
尽可能使用 asset 结构体进行运算,而不是把 balance 提取出来进行运算。
真实案例
权限校验
在进行相关操作时,应严格判断函数入参和实际调用者是否一致,使用 require_auth
进行校验。
漏洞示例
存在缺陷的代码: transfer
转账
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 ); }
防御方法
使用 require_auth( from )
校验资产转出账户与调用账户是否一致。
真实案例
暂无
apply 校验
在处理合约调用时,应确保每个 action 与 code 均满足关联要求。
漏洞示例
存在缺陷的代码:
// 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))
防御方法
使用
if( ((code == self && action != N(transfer) ) || (code == N(eosio.token) && action == N(transfer)) || action == N(onerror)) ) { }
绑定每个关键 action 与 code 是否满足要求,避免异常调用。
真实案例
参考文献
- 保管好私钥就安全了吗?注意隐藏在EOS DAPP中的安全隐患
- 漏洞详解|恶意 EOS 合约存在吞噬用户 RAM 的安全风险
- How EOSBET attacked by aabbccddeefg
- BET被黑客攻击始末,实锤还原作案现场和攻击手段
- 累计薅走数百万,EOS Dapps已成黑客提款机?
致谢
- 麒麟工作组
- eosiofans
- 荆凯(EOS42)
- 星魂
- 岛娘
- 赵余(EOSLaoMao)
- 字符
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
创业之初你不可不知的融资知识
桂曙光 / 机械工业出版社 / 2010-6-1 / 48.00元
从零到精通 成功融资必读书 像小说一样好看的趣味融资书 手把手教你找到VC拿到钱 本书以创业者寻找风险投资的逻辑顺序为主线,运用理论分析和实例剖析相结合的手法,将简洁、通俗的语言与丰富的图表工具相结合,辅以中肯的建议,同时运用大量鲜活的、有代表性的成败案例,为读者解读创业之初企业有效成功融资的途径和方法,帮助你的企业开创新的辉煌。一起来看看 《创业之初你不可不知的融资知识》 这本书的介绍吧!