美文网首页
【慢雾出品】EOS 智能合约最佳安全开发指南

【慢雾出品】EOS 智能合约最佳安全开发指南

作者: 228b5dff4508 | 来源:发表于2018-09-21 17:20 被阅读427次
    image

    本文安全实践合集,由慢雾(SlowMist)同学主导,麒麟小组以及其他同学参与,结合近期爆发的安全热点问题进行实战说明,主体内容涵盖了安全准则以及常见已知漏洞(数值溢出、权限校验、apply 校验)说明,旨在为EOS社区积累良好安全的智能合约开发实践经验,为 EOS 智能合约开发人员提供一些智能合约的安全准则及已知漏洞分析。我们邀请社区对该文档提出修改或完善建议,欢迎各种合并请求(Pull Request)。若有相关的文章或博客的发表,也请将其加入到参考文献中。

    中文版:

    https://github.com/slowmist/eos-smart-contract-security-best-practices

    英文版:

    https://github.com/slowmist/eos-smart-contract-security-best-practices/blob/master/README_EN.md

    目录

    • 安全准则

    • 已知漏洞

        1.  数值溢出
      
              1.1 漏洞示例
      
              1.2 防御方法
      
              1.3 真实案例
      
         2. 权限校验
      
               2.1 漏洞示例
      
               2.2 防御方法
      
               2.3 真实案例
      
          3\. apply 校验 
      
               3.1 漏洞示例
      
               3.2 防御方法
      
               3.3 真实案例
      
    • 参考文献

    • 致谢

    安全准则

    EOS 处于早期阶段并且有很强的实验性质。因此,随着新的 bug 和安全漏洞被发现,新的功能不断被开发出来,其面临的安全威胁也是不断变化的。这篇文章对于开发人员编写安全的智能合约来说只是个开始。

    开发智能合约需要一个全新的工程思维,它不同于我们以往项目的开发。因为它犯错的代价是巨大的,很难像中心化类型的软件那样,打上补丁就可以弥补损失。就像直接给硬件编程或金融服务类软件开发,相比于 Web 开发和移动开发都有更大的挑战。因此,仅仅防范已知的漏洞是不够的,还需要学习新的开发理念:

    • 对可能的错误有所准备。

    任何有意义的智能合约或多或少都存在错误,因此你的代码必须能够正确的处理出现的 bug 和漏洞。需始终保证以下规则:

    1.当智能合约出现错误时,停止合约

    2.管理账户的资金风险,如限制(转账)速率、最大(转账)额度

    3.有效的途径来进行 bug 修复和功能提升

    • 谨慎发布智能合约。

    尽量在正式发布智能合约之前发现并修复可能的 bug。

    1.对智能合约进行彻底的测试,并在任何新的攻击手法被发现后及时的测试(包括已经发布的合约)

    2.从 alpha 版本在麒麟测试网(CryptoKylin-Testnet)上发布开始便邀请专业安全审计机构进行审计,并提供漏洞赏金计划(Bug Bounty)

    3.阶段性发布,每个阶段都提供足够的测试

    • 保持智能合约的简洁。

    复杂会增加出错的风险。

    1.确保智能合约逻辑简洁

    2.确保合约和函数模块化

    3.使用已经被广泛使用的合约或工具(比如,不要自己写一个随机数生成器)

    4.条件允许的话,清晰明了比性能更重要

    5.只在你系统的去中心化部分使用区块链

    • 保持更新。

    通过公开资源来确保获取到最新的安全进展。

    1.在任何新的漏洞被发现时检查你的智能合约

    2.尽可能快的将使用到的库或者工具更新到最新

    3.使用最新的安全技术

    • 清楚区块链的特性。

    尽管你先前所拥有的编程经验同样适用于智能合约开发,但这里仍然有些陷阱你需要留意:

    require_recipient(account_name name) 可触发通知,调用name合约中的同名函数,官方文档

    已知漏洞

    数值溢出

    在进行算术运算时,未进行边界检查可能导致数值上下溢,引起智能合约用户资产受损。

    1.漏洞示例

    存在缺陷的代码:batchTransfer 批量转账

    <pre style="margin: 0px; padding: 16px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; overflow-wrap: normal; overflow: auto; line-height: 1.45; background-color: rgb(246, 248, 250); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; color: rgb(36, 41, 46);">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);
    

    }</pre>

    2.防御方法

    尽可能使用 asset 结构体进行运算,而不是把 balance 提取出来进行运算。

    3.真实案例

    【EOS Fomo3D你千万别玩】狼人杀遭到溢出攻击, 已经凉凉

    权限校验

    在进行相关操作时,应严格判断函数入参和实际调用者是否一致,使用require_auth进行校验。

    漏洞示例

    存在缺陷的代码:transfer 转账

    <pre style="margin: 0px; padding: 16px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; overflow-wrap: normal; overflow: auto; line-height: 1.45; background-color: rgb(246, 248, 250); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; color: rgb(36, 41, 46);">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 );
    

    }</pre>

    防御方法

    使用require_auth( from )校验资产转出账户与调用账户是否一致。

    真实案例

    暂无

    apply 校验

    在处理合约调用时,应确保每个 action 与 code 均满足关联要求。

    1.漏洞示例

    存在缺陷的代码:

    <pre style="margin: 0px; padding: 16px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 13.6px; overflow-wrap: normal; overflow: auto; line-height: 1.45; background-color: rgb(246, 248, 250); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; color: rgb(36, 41, 46);">// 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))</pre>

    2.防御方法

    使用

    绑定每个关键 action 与 code 是否满足要求,避免异常调用

    3.真实案例

    EOSBet 黑客攻击事件复盘

    参考文献

    • 保管好私钥就安全了吗?注意隐藏在EOS DAPP中的安全隐患

    • 漏洞详解|恶意 EOS 合约存在吞噬用户 RAM 的安全风险

    • How EOSBET attacked by aabbccddeefg

    • BET被黑客攻击始末,实锤还原作案现场和攻击手段

    • 累计薅走数百万,EOS Dapps已成黑客提款机?

    致谢

    • 麒麟工作组

    • eosiofans

    • 荆凯(EOS42)

    • 星魂

    • 岛娘

    • 赵余(EOSLaoMao)

    • 字符

    相关文章

      网友评论

          本文标题:【慢雾出品】EOS 智能合约最佳安全开发指南

          本文链接:https://www.haomeiwen.com/subject/qulznftx.html