Upload
others
View
7
Download
0
Embed Size (px)
Citation preview
1
公链安全审计报告
2
审计编号:201905231848
审计公链名称:YOYOW
YOYOW审计开始日期:2019.4.10
审计完成日期:2019.5.23
审计结果:通过
审计团队:成都链安科技有限公司
审计类型及结果:
审计项目(资金部分) 审计结果
1 公链资金(YOYO,IOU)安全 通过
2 交易消耗机制(积分)审计 通过
3 挖矿激励机制(代币回报)审计 通过
4 密钥体系安全 通过
5 支付机制(包括配套)审计 通过
6 账号体系安全 通过
免责声明:本次审计仅针对审计项目中给定的审计范围进行审计,其他未知功能安全漏洞不在本次审
计责任范围之内,亦不对其承担责任。本报告所作的安全审计分析及其他内容,仅基于公链提供者截
至本报告出具时向成都链安科技提供的文件和资料,文件和资料不应存在缺失、被篡改、删减或隐瞒
的情形;如已提供的文件和资料存在信息缺失、被篡改、删减、隐瞒或反映的情况与实际情况不符等
情况,成都链安科技对由此而导致的损失和不利影响不承担任何责任。本声明最终解释权归成都链安
科技所有。
3
目录
1 前言...................................................................................................................................................................5
2 项目背景...........................................................................................................................................................5
3 审计内容...........................................................................................................................................................5
3.1 区块链生产及治理过程........................................................................................................................7
3.1.1 见证人........................................................................................................................................ 7
3.1.2 理事会........................................................................................................................................ 9
3.1.3 投票与投票代理......................................................................................................................10
3.1.4 安全性分析..............................................................................................................................10
3.2 公链资产安全......................................................................................................................................12
3.2.1 YOYO asset............................................................................................................................. 13
3.2.2 用户 IOU.................................................................................................................................. 14
3.2.3 积分..........................................................................................................................................15
3.2.4 公链资产安全风险分析..........................................................................................................16
3.3 账号体系安全......................................................................................................................................19
3.3.1 密钥的体系..............................................................................................................................19
3.3.2 密钥的生成..............................................................................................................................20
3.3.3 账户的注册与秘钥存储..........................................................................................................21
3.3.4 账户的分类..............................................................................................................................23
3.3.5 交易签名与鉴权......................................................................................................................23
3.4 应用层功能..........................................................................................................................................26
3.4.1 平台 platform.......................................................................................................................... 27
3.4.2 文章及打赏..............................................................................................................................27
3.4.3 广告..........................................................................................................................................27
3.4.4 用户自定义投票......................................................................................................................28
4
3.4.5 安全性分析..............................................................................................................................28
4 审计结果.........................................................................................................................................................30
4.1 operation权限校验存在绕过风险....................................................................................................30
4.2 client钱包的安全配置问题............................................................................................................... 33
4.3 代码静态扫描及 fuzz测试结果.........................................................................................................35
5
1 前言
成都链安团队于 2019年 4月 10日收到 yoyow团队的审计申请,本次审计从 4月 10日
开始到 5月 23日结束
2 项目背景
YOYOW是使用去中心化的共识方式为内容生产领域进行贡献定价的区块链网络
项目官网:
https://yoyow.org/
代码仓库:
https://github.com/yoyow-org/yoyow-core
审计版本:
Testnet
Commit:65aca7f855ddaccaf7e2c30776b4d595da7e4853
主要编码语言:
C++
3 审计内容
YOYOW所有应用层功能均由下列 operation实现
transfer_operation
account_create_operation
account_manage_operation
account_update_auth_operation
account_update_key_operation
account_update_proxy_operation
csaf_collect_operation
csaf_lease_operation
committee_member_create_operation
6
committee_member_update_operation
committee_member_vote_update_operation
committee_proposal_create_operation
committee_proposal_update_operation
witness_create_operation
witness_update_operation
witness_vote_update_operation
witness_collect_pay_operation
witness_report_operation
post_operation
post_update_operation
platform_create_operation
platform_update_operation
platform_vote_update_operation
account_auth_platform_operation
account_cancel_auth_platform_operation
asset_create_operation
asset_update_operation
asset_issue_operation
asset_reserve_operation
asset_claim_fees_operation
override_transfer_operation
proposal_create_operation // 已禁用
proposal_update_operation // 已禁用
proposal_delete_operation // 已禁用
account_enable_allowed_assets_operation
account_update_allowed_assets_operation
account_whitelist_operation
score_create_operation
7
reward_operation
reward_proxy_operation
buyout_operation
license_create_operation
advertising_create_operation
advertising_update_operation
advertising_buy_operation
advertising_confirm_operation
advertising_ransom_operation
custom_vote_create_operation
custom_vote_cast_operation
3.1 区块链生产及治理过程
witness_create_operation
witness_update_operation
witness_collect_pay_operation
witness_report_operation
witness_vote_update_operation
committee_member_create_operation
committee_member_update_operation
committee_proposal_create_operation
committee_proposal_update_operation
committee_member_vote_update_operation
account_update_proxy_operation
3.1.1 见证人
1. 见证人插件启动过程
8
2. 见证人操作
witness_create_operation
witness_update_operation
witness_collect_pay_operation
witness_report_operation
用户通过 witness相关 operation进行候选见证人的创建、更新、获得见证人出块报酬以
及对恶意见证人的举报。
9
如果见证人长时间离线超过一定限度,将其设置为暂时离线状态。见证人错过的块不能
超过全局参数中定义的max_witness_inactive_blocks
void database::update_global_dynamic_data( const signed_block& b ){
//....modify( witness_missed, [&]( witness_object& w ) {
w.total_missed++;if( w.last_confirmed_block_num +
gpo.parameters.max_witness_inactive_blocks < b.block_num() )w.signing_key = public_key_type();
});//....
}
见证人调度的过程中,签名公钥为 public_key_type()的见证人不会加入到活跃见证人中
if( top_itr->signing_key != public_key_type() ){
new_witnesses.insert( std::make_pair( top_itr->account,scheduled_by_vote_top ) );}
见证人举报操作 witness_report_operation,举报见证人制造分叉,举报区块一和区块二
的时间戳、见证人、区块号、见证人签名相同,区块 id不同
3.1.2 理事会
1. 理事会调度
每个区块生产时,都会调用 update_committee(),判断是否到理事会成员更新时间(由
全局参数指定,30天)
//按票数选取新的理事会
const auto& top_idx =get_index_type<committee_member_index>().indices().get<by_votes>();auto top_itr = top_idx.lower_bound( true );while( top_itr != top_idx.end() && new_committee.size() <gpo.parameters.committee_size ){
new_committee.insert( top_itr->account );++top_itr;
}
2. 理事会操作
10
committee_member_create_operation
committee_member_update_operation
committee_proposal_create_operation
committee_proposal_update_operation
用户通过 committee相关 operation进行候选理事会的创建、更新、创建提案以及给提案
投票,提案通过的阈值定义在 config.hpp中,不同提案类型阈值不同。
3.1.3 投票与投票代理
witness_vote_update_operation
account_update_proxy_operation
普通账户可以通过投票的来票选理事会和见证人(主力和备选),可以设置指定账户为
投票代理。
3.1.4 安全性分析
本部分未发现安全性风险
1. 权限校验
本部分 operation均进行了完整的权限校验
2. 见证人、理事会对象相关操作校验
见证人的创建和更新的 operation相关检查包括押金资产符号检查、用户权限校验和可用
余额的检查。
//押金资产符号检查
FC_ASSERT( asset_obj, "Could not find asset matching ${asset}", ("asset",pledge_asset_symbol) );
//可用余额/押金检查
FC_ASSERT( available_balance >= op.pledge.amount,"Insufficient Balance: account ${a}'s available balance of ${b}
is less than required ${r}",("a",op.account)("b",d.to_pretty_core_string(available_balance))("r",d.to_pretty_string(op.pledge)) );
见证人更新(witness_update_operation)操作中,获取要更新的见证人对象时使用的是
get_witness_by_uid,只获取 by_valid字段为 true的见证人对象,避免了已经退出见证
人(押金设为 0)但数据库尚未完全删除该见证人对象时被错误读取,理事会账户同理。
11
const witness_object& database::get_witness_by_uid( account_uid_typeuid )const{
const auto& idx =get_index_type<witness_index>().indices().get<by_valid>();
auto itr = idx.find( std::make_tuple( true, uid ) );FC_ASSERT( itr != idx.end(), "witness ${uid} not found.", ("uid",uid) );return *itr;
}
3. 举报见证人操作校验
能有效防止用户恶意举报见证人
FC_ASSERT( first_block.timestamp == second_block.timestamp, "first block andsecond block should have same timestamp" );FC_ASSERT( first_block.witness == second_block.witness, "first block andsecond block should be produced by same witness" );FC_ASSERT( first_block.id() != second_block.id(), "first block and secondblock should be different" );FC_ASSERT( first_block.block_num() > 0, "reporting block number should bepositive" );FC_ASSERT( first_block.block_num() == second_block.block_num(), "first blockand second block should have same block number" );FC_ASSERT( first_block.signee() == second_block.signee(), "first block andsecond block should be signed by same key" );
4. 理事会提案操作校验
正式理事会成员可以创建修改用户权限、操作费率、全局参数的提案。理事会提案结构
设置投票关闭的块号 voting_closing_block_num,并且每一次理事会调度就会删除提
案,避免了暂时未通过的提案被高票数理事会成员重复投票。
//验证发起提案的账户在不在 active_committee_members(正式理事会成员)内
const auto& current_committee = gpo.active_committee_members;FC_ASSERT( current_committee.find( op.proposer ) != current_committee.end(),"Account ${a} is not an active committee member", ("a",op.proposer));
//提案投票关闭的块号应该大于等于当前块号
FC_ASSERT( op.voting_closing_block_num >= d.head_block_num(),"Voting closing block number should not be earlier than head
block number" );//提案投票关闭的块号应该小于等于(早于)下一次正式理事会更新的块号
FC_ASSERT( op.voting_closing_block_num <= dpo.next_committee_update_block,
12
"Voting closing block number should not be later than nextcommittee update block number" );//提案执行的块号应该小于等于下一次正式理事会成员更新的块号
FC_ASSERT( op.execution_block_num <= dpo.next_committee_update_block,"Proposal execution block number should not be later than next
committee update block number" );//提案过期的块号应该小于等于下一次正式理事会成员更新的块号
FC_ASSERT( op.expiration_block_num <= dpo.next_committee_update_block,"Proposal expiration block number should not be later than next
committee update block number" );
理事会成员给提案投票校验
//投票账户要为正式理事会
FC_ASSERT( current_committee.find( op.account ) !=current_committee.end(),"Account ${a} is not an active committeemember",("a",op.account) );
//提案还未结束
FC_ASSERT( d.head_block_num() <= proposal_obj->voting_closing_block_num,"Voting for proposal ${n} has closed, can not vote",("n",op.proposal_number) );
//不能重复给一个提案表达两次相同的意见,第二次为同一提案表态必须跟上一次不同
auto itr = proposal_obj->opinions.find( op.account );if( itr != proposal_obj->opinions.end() ){
const auto old_opinion = itr->second;FC_ASSERT( old_opinion != op.opinion, "Opinion on proposal ${n} did not
change.", ("n",op.proposal_number) );}
5. 投票操作检查
投过票(is_voter为 true)才能当代理人,不能设置自己为代理人
A如果设置 B为代理那么 B就不能设置 A为代理
代理层级最多max_governance_voting_proxy_level,超出限度同样可以用
set_voting_proxy设置代理,但投票将不会记录有效票数
3.2 公链资产安全
transfer_operation
13
asset_create_operation
asset_update_operation
asset_issue_operation
asset_reserve_operation
asset_claim_fees_operation
override_transfer_operation
csaf_collect_operation
csaf_lease_operation
3.2.1 YOYO asset
平台 YOYO通过 genesis创世文件 init,总量 10亿,assetid为 0; YOYO的余额和零钱交
易可以通过 transfer_operation进行转账
YOYO资产抵押以及赎回:要成为见证人,理事会,平台等角色,需要用户抵押 YOYO
代币,才能获得,抵押资产会记录进不同变量中:
auto available_balance = from_stats->core_balance//YOYO 总额
- from_stats->core_leased_out// 积分租借
- from_stats->total_witness_pledge//见证人抵押
- from_stats->total_platform_pledge//平台抵押
- from_stats->total_committee_member_pledge;//理事会抵押
当用户取消或者减少抵押后,存在赎回逻辑,以及赎回延时:
取消抵押时设置赎回额度以及赎回延时:
else if( op.new_pledge->amount == 0 ) // resign{
d.modify( *account_stats, [&](account_statistics_object& s) {s.releasing_platform_pledge = s.total_platform_pledge;//赎回额度
s.platform_pledge_release_block_number = d.head_block_num() +global_params.platform_pledge_release_delay;//赎回延时,一般为 1 天
});d.modify( *platform_obj, [&]( platform_object& pfo ) {
pfo.is_valid = false; // Processing will be delayed});d.modify( *account_obj, [&]( account_object& acc ){
acc.is_full_member = false;});
14
}
当达到赎回延时后,在 apply_block中的 release_xxx_pledges 执行赎回逻辑:
void database::release_platform_pledges(){
const auto head_num = head_block_num();const auto& idx =
get_index_type<account_statistics_index>().indices().get<by_platform_pledge_release>();
auto itr = idx.begin();while( itr != idx.end() && itr->platform_pledge_release_block_number <=
head_num ){
modify( *itr, [&](account_statistics_object& s) {s.total_platform_pledge -= s.releasing_platform_pledge;s.releasing_platform_pledge = 0;s.platform_pledge_release_block_number = -1;
});itr = idx.begin();
}}
3.2.2 用户 IOU
账户通过 asset相关 operation进行代币创建,更新,分发等操作,也可以通过
transfer_operation转账;用户可以配置接收代币的种类,以及设置白名单账户,可以接
收白名单账户所有 asset转账
IOU相关 operation:
asset_create_operation
asset_update_operation
asset_issue_operation
asset_reserve_operation
asset_claim_fees_operation
override_transfer_operation
account_enable_allowed_assets_operation
account_update_allowed_assets_operation
15
account_whitelist_operation
IOU相关 operation权限校验部分存在缺失,只校验 fee payer权限,在 fee为 0时(需
要一半以上理事会同意),会造成权限校验失效,影响所有 IOU用户发行的资产
例如:issue operation,缺少 get_required_xxx_uid_authorities获取所需权限
当 fee=0,不会校验 fee payer权限,
if( v.fee.total.amount == 0 ){
// don't need additional authority for zero fee operations.}
struct asset_issue_operation : public base_operation{
struct fee_parameters_type {uint64_t fee = 20 * GRAPHENE_BLOCKCHAIN_PRECISION;uint32_t price_per_kbyte = GRAPHENE_BLOCKCHAIN_PRECISION; ///< Only
for memouint64_t min_real_fee = 0;uint16_t min_rf_percent = 0;extensions_type extensions;
};
fee_type fee;account_uid_type issuer; ///< Must be asset_to_issue->asset_id-
>issuerasset asset_to_issue;account_uid_type issue_to_account;
/** user provided data encrypted to the memo key of the "to" account */optional<memo_data> memo;extensions_type extensions;
account_uid_type fee_payer_uid()const { return issuer; }void validate()const;share_type calculate_fee(const fee_parameters_type& k)const;
};
3.2.3 积分
Yoyow积分为 csaf,积分主要用于抵扣手续费,文章打分;
16
积分主要通过 YOYO代币的持有来获取,累计 YOYO币龄可以获得积分,然后提取至账
户中:
csaf相关 operation:
csaf_collect_operation
csaf_lease_operation
3.2.4 公链资产安全风险分析
IOU部分 operation权限存在安全风险已经指出。除去调用权限问题,公链资产部分主要
考虑溢出,以及传参合法性;这部分校验比较完善,未发现安全问题
1. 溢出防范
Asset 结构体内 amount使用的是 fc::safe定义 int64,由于 fc::safe重载了运算符,并且
做了溢出防范,所以在 asset运算中不会发生溢出。
2. 传参合法性
17
由于是 int64类型,存在负数,所以进行 asset相关操作时需要校验传入参数>0, 防止用
户恶意传入负数,这在几乎所有 operation的 validate中均有校验,少数如平台授权额度
中未校验,但不影响安全性:
void transfer_operation::validate()const{
validate_op_fee( fee, "transfer " );validate_account_uid( from, "from " );validate_account_uid( to, "to " );validate_positive_asset( amount, "transfer amount" );//校验 amount 合法性
void validate_positive_amount( share_type amount, const string& object_name ){
FC_ASSERT( amount > 0, "${o} should be positive.", ("o", object_name) );}
抵押或租借时可用余额判断:
当需要租借或抵押时需要计算 available_balance,扣除已抵押或租借的金额
if( delta > 0 ){
auto available_balance = from_stats->core_balance- from_stats->core_leased_out- from_stats->total_witness_pledge- from_stats->total_platform_pledge- from_stats->total_committee_member_pledge;
FC_ASSERT( available_balance >= delta,"Insufficient Balance: account ${a}'s available balance of
${b} is less than required ${r}",("a",op.from)("b",d.to_pretty_core_string(available_balance))("r",d.to_pretty_core_string(delta)) );//可用余额判断
}
资产 asset的增减通过 adjust_balance方法进行加减,并在内部判断是否足够进行操作
void database::adjust_balance(account_uid_type account, asset delta ){ try {
if( delta.amount == 0 )return;
18
auto& index =get_index_type<account_balance_index>().indices().get<by_account_asset>();
auto itr = index.find(boost::make_tuple(account, delta.asset_id));if(itr == index.end()){
FC_ASSERT( delta.amount > 0, "Insufficient Balance: account ${a}'sbalance of ${b} is less than required ${r}",
("a",account)("b",to_pretty_string(asset(0,delta.asset_id)))("r",to_pretty_string(-delta)) );
create<account_balance_object>([account,&delta](account_balance_object&b) {
b.owner = account;b.asset_type = delta.asset_id;b.balance = delta.amount.value;
});} else {
if( delta.amount < 0 )FC_ASSERT( itr->get_balance() >= -delta,
"Insufficient Balance: account ${a}'s balance of ${b} isless than required ${r}",
("a",account)("b",to_pretty_string(itr->get_balance()))("r",to_pretty_string(-delta)) );//判断余额是否足够
modify(*itr, [delta](account_balance_object& b) {b.adjust_balance(delta);
});}
csaf和 prepaid则在 do_evaluate也会校验
FC_ASSERT(account_stats.prepaid >= op.amount.amount,"Insufficient balance: unable to transfer, because the
account ${a} `s prepaid [${c}] is less than needed [${n}]. ",("c", (account_stats.prepaid))("a", op.from)("n",
op.amount.amount));FC_ASSERT(account_stats->csaf >= op.csaf,
"Insufficient csaf: unable to score, because account: ${f}`s member points [${c}] is less than needed [${n}]",
("f",op.from_account_uid)("c",account_stats->csaf)("n",op.csaf));
交易手续费合法性校验
交易手续费由于可以由多参数组成,其中需要校验参数正负以及合法性
校验逻辑:
19
void validate_op_fee( const asset& fee, const string& op_name ){
validate_non_negative_core_asset( fee, op_name + "fee" );//fee>0}void validate_op_fee( const fee_type& fee, const string& op_name ){
validate_op_fee( fee.total, op_name + "total ");if( fee.options.valid() ){
const auto& fov = fee.options->value;asset total; // 0 COREif( fov.from_balance.valid() ){
validate_op_fee( *fov.from_balance, op_name + "from_balance " );total += *fov.from_balance;
}if( fov.from_prepaid.valid() ){
validate_op_fee( *fov.from_prepaid, op_name + "from_prepaid " );total += *fov.from_prepaid;
}if( fov.from_csaf.valid() ){
validate_op_fee( *fov.from_csaf, op_name + "from_csaf " );total += *fov.from_csaf;
}FC_ASSERT( total.amount == fee.total.amount, "${o}total fee should be
equal to sum of fees in options.", ("o", op_name) );//amount=total}
}
3.3 账号体系安全
account_create_operation,
account_manage_operation,
account_update_auth_operation,
account_update_key_operation,
3.3.1 密钥的体系
区块链中的权限控制是以公私钥对为基本单位,本链制定了四种不同的密钥对用于
划分四种不同的用户权限,其划分如下:
20
主控密钥(owner_key):为最高权限密钥,可以对账户进行任何操作。(一般只用
于恢复账户)。
资金密钥(active_key):用于操作账户的余额。
零钱密钥(secondary_key):用于操作账户零钱,执行发帖、点赞、登陆鉴权等操
作。
备注密钥(memo_key):加密交易附带的备注信息。
账户钱包里存储的不同权限类型的公私钥对是判断用户是否有权限进行相关操作的唯一
依据。
3.3.2 密钥的生成
使用密钥生成命令 suggest_brain_key可生成一组包含一串助记词
(brain_priv_key)和一对公私钥(wif_priv_key、pub_key)的密钥对:
brain_key = normalize_brain_key(brain_key);fc::ecc::private_key priv_key = derive_private_key( brain_key, 0 );result.brain_priv_key = brain_key;result.wif_priv_key = key_to_wif( priv_key );result.pub_key = priv_key.get_public_key();return result;
21
三者的关系如下:
brain_key: 助记词,在预定义的词组(libraries/utilities/words.cpp)里面随机选取 16个词
作为 brain_key。可用来生成密钥对。
private_key: 以 brain_key作为随机种子,使用 hash512和 hash256算法生成
private_key(对用户透明)。
wif_priv_key:由 private_key经过特殊字符处理, 然后 base58编码后,形成固定长度的字
符串(作为用户的私钥)。
pub_key: 根据 private_key得到 pub_key(作为用户的公钥)。
3.3.3 账户的注册与秘钥存储
用户可以有两种方式注册账户:个人注册、委托第三方注册商注册。
1. 个人注册
个人可以调用 wallet提供的 API接口:create_account_with_brain_key 注册个人账
户(需要一个已存在的账户为其支付费用)。具体过程:
22
使用 create_account_with_brain_key命令创建账户的时候, 会把用户的 active_key
的公私钥対存入内存中,并把 active_prive_key使用钱包密码 hash加密存放在
wallet.json中的 cipher_keys字段。
如果用户需要使用 owner_key权限,就需要自己使用 import_key导入 owner_key的
私钥。
2. 委托第三方注册商注册
在用户不方便自己注册账户时,可以提供自己的公钥(owner_pub_key、
active_pub_key)提供给注册商委托其注册。
使用 register_account命令创建账户的时候,客户端不存储任何 key在本地磁盘和内
存里面。(因为该命令一般是为注册商提供的,注册商不应该拥有账户的私钥)。
23
3.3.4 账户的分类
该链根据用户的不同角色,分为不同的账户类型:
3.3.5 交易签名与鉴权
1. 客户端签名
Wallet客户端对发起的每一笔交易都要用账户对应权限的私钥进行交易签名,以防止交
易数据被篡改和伪造。具体实现如下:
24
发送交易之前, 首先会根据用户 uid和交易的类型 operation 去远程判断交易所需的
权限对应的用户公钥并返回(required_key) 。然后在钱包内存里面找 _key 变量里面是否
有对应公钥的私钥。
if( required_keys.find( public_key_type() ) == required_keys.end() ){
// get a subset of available keysflat_set<public_key_type> available_keys;flat_map<public_key_type,fc::ecc::private_key> available_keys_map;
//循环判断秘钥., 客户端验证, 在_key 里面去找 公钥
for( const auto& pub_key : required_keys ){
auto it = _keys.find( pub_key );if( it != _keys.end() ){
fc::optional<fc::ecc::private_key> privkey = wif_to_key( it->second );
//判断私钥格式
FC_ASSERT( privkey.valid(), "Malformed private key in _keys" );available_keys.insert( pub_key );//定义一个公私钥对的 mapavailable_keys_map[ pub_key ] = *privkey;
}}
25
如果存在公钥对应的私钥,就认为用户有权限操作,然后使用该私钥进行交易签名,最
后广播交易。
2. 节点验证签名
节点在收到交易之后,会对交易内容以及有效性进行判断,其中一项就是验证该交易签
名的有效性,具体如下:
verify_authority会调用 operation_get_required_uid_authorities,代码如下
void operation_get_required_uid_authorities( const operation& op,flat_set<account_uid_type>&
owner_uids,flat_set<account_uid_type>&
active_uids,flat_set<account_uid_type>&
secondary_uids,vector<authority>& other )
{op.visit( operation_get_required_uid_auth( owner_uids, active_uids,
secondary_uids, other ) );}
26
不同的 operation获取不同的权限,所需权限在 operation中定义,例如
account_create_operation。operation_get_required_uid_auth调用 operation的
get_required_(active|owner|secondary)_uid_authorities和 fee_payer_uid()进行权限校
验
struct account_create_operation : public base_operation{
//...其他结构体代码
account_uid_type fee_payer_uid()const { return reg_info.registrar; }
void get_required_active_uid_authorities( flat_set<account_uid_type>&a )const
{// registrar should be required anyway as it is the fee_payer_uid(),// but we insert it here because fee can be paid with secondary
authoritya.insert( reg_info.registrar );
}};
3.4 应用层功能
platform_create_operation
platform_update_operation
platform_vote_update_operation
post_operation
post_update_operation
account_auth_platform_operation
account_cancel_auth_platform_operation
score_create_operation
reward_operation
reward_proxy_operation
buyout_operation
license_create_operation
advertising_create_operation
advertising_update_operation
advertising_buy_operation
27
advertising_confirm_operation
advertising_ransom_operation
custom_vote_create_operation
custom_vote_cast_operation
3.4.1 平台 platform
普通账户需要抵押足够的押金才能成为平台账户,平台账户拥有创建文章,使用授权用
户零钱的权限
平台相关 operation:
platform_create_operation
platform_update_operation
platform_vote_update_operation
account_auth_platform_operation
account_cancel_auth_platform_operation
3.4.2 文章及打赏
用户授权平台后,使用平台+用户权限即可发布文章,文章发布后用户可以进行评分以及
打赏,所得收益平台与文章受益人进行分配。文章收益权限可以被买断
相关 operation:
score_create_operation
reward_operation
reward_proxy_operation
buyout_operation
license_create_operation
3.4.3 广告
平台可以发布广告位置,定义价格,用户按时长进行购买
相关 operation:
28
advertising_create_operation
advertising_update_operation
advertising_buy_operation
advertising_confirm_operation
advertising_ransom_operation
3.4.4 用户自定义投票
用户可以创建自定义投票,由其他用户进行投票,并可以设置投票门槛
相关 operation:
custom_vote_create_operation,
custom_vote_cast_operation
3.4.5 安全性分析
本部分未发现安全风险;
1. 权限校验
本部分 operation均进行了完整的权限校验
2. 资产相关操作校验
资产相关操作均进行了合法性和完备性校验,用户授权平台额度未做>0校验,但不影响
安全性
如平台抵押:
FC_ASSERT( op.pledge.amount >= global_params.platform_min_pledge,"Insufficient pledge: provided ${p}, need ${r}",("p",d.to_pretty_string(op.pledge))
("r",d.to_pretty_core_string(global_params.platform_min_pledge)) );//大于最小
抵押金额
auto available_balance = account_stats->core_balance- account_stats->core_leased_out- account_stats->total_committee_member_pledge- account_stats->total_witness_pledge;
FC_ASSERT( available_balance >= op.pledge.amount,
29
"Insufficient Balance: account ${a}'s available balance of ${b}is less than required ${r}",
("a",op.account)("b",d.to_pretty_core_string(available_balance))("r",d.to_pretty_string(op.pledge)) );
const platform_object* maybe_found =d.find_platform_by_owner( op.account );
FC_ASSERT( maybe_found == nullptr, "This account already has aplatform" );//可用金额足够
3. 打赏收益分配校验
对打赏分配百分比,以及买断进行了校验限制:
if (ext.receiptors.valid()){
const map<account_uid_type, Recerptor_Parameter>& receiptor =*(ext.receiptors);
FC_ASSERT(receiptor.size() >= 1 && receiptor.size() <= 5,"receiptors` size must be >= 1 and <= 5");//限制收益人数量
if (platform == poster){auto itor = receiptor.find(platform);FC_ASSERT(itor != receiptor.end(), "platform must be included
by receiptors");FC_ASSERT(itor->second.cur_ratio >=
GRAPHENE_DEFAULT_PLATFORM_RECERPTS_RATIO +GRAPHENE_DEFAULT_POSTER_MIN_RECERPTS_RATIO,
"platform`s ratio must be ${n}%", ("n",(GRAPHENE_DEFAULT_PLATFORM_RECERPTS_RATIO +GRAPHENE_DEFAULT_POSTER_MIN_RECERPTS_RATIO) / 100));
}else{
auto itor = receiptor.find(platform);FC_ASSERT(itor != receiptor.end(), "platform must be included
by receiptors");FC_ASSERT(itor->second.cur_ratio ==
GRAPHENE_DEFAULT_PLATFORM_RECERPTS_RATIO, "platform`s ratio must be ${n}%",("n", GRAPHENE_DEFAULT_PLATFORM_RECERPTS_RATIO / 100));
auto itor_poster = receiptor.find(poster);FC_ASSERT(itor_poster != receiptor.end(), "poster must be
included by receiptors");FC_ASSERT(itor_poster->second.cur_ratio >=
GRAPHENE_DEFAULT_POSTER_MIN_RECERPTS_RATIO, "poster`s ratio must be >= ${n}%",("n", GRAPHENE_DEFAULT_POSTER_MIN_RECERPTS_RATIO / 100));
}
30
uint32_t total = 0;//uint32 可以防止 uint16 相加溢出,cur_ratio 为
uint16for (auto iter : receiptor){
validate_account_uid(iter.first, "receiptor");iter.second.validate();total += iter.second.cur_ratio;
}FC_ASSERT(total == GRAPHENE_100_PERCENT, "The sum of receiptors`
ratio must be 100%");//收益总百分比==100%}
买断校验:
FC_ASSERT(iter->second.cur_ratio >= *(ext_para->buyout_ratio),"the ratio ${r} of receiptor ${p} is less than sell
${sp} .",("r", iter->second.cur_ratio)("p", *(ext_para-
>receiptor))("sp", *(ext_para->buyout_ratio)));//可出售收益<=cur_ratio
4 审计结果
本公司采用静态分析、动态分析、典型案例测试和人工审核的方式对公链 YOYOW的代
码规范性、安全性以及业务逻辑三个方面进行多维度全面的安全审计。存在安全风险如
下:
4.1 operation 权限校验存在绕过风险
文件路径:libraries\chain\include\graphene\chain\protocol\asset_ops.hpp
这部分 operation缺少 get_required_xxx_uid_authorities获取需要权限,所有权限检查
依赖于 fee payer;当 fee为 0时会导致这部分操作不需要权限检查,主要影响用户 iou
资产操作(理事会拥有修改 fee的权限和功能,超过一半理事会同意即可修改 fee)
测试 API asset_reserve_operation,对应 API reserve_asset
signed_transaction reserve_asset(string from,string amount,string symbol,
31
bool csaf_fee,bool broadcast /* = false */)
测试过程:
1. 修改代码 libraries\wallet\wallet.cpp的 sign_transaction函数,构造一个签名的
transaction,其中 testnet和 testnet1为已有的账户
2. 重新编译 yoyow_client
3. 在本地测试网上使 asset_reserve_operation费用为 0的提案通过
4. 导入 testnet权限,import_key testnet [private key]
32
5. 使用 reserve_asset销毁 test账户资产
未加权限校验的 API统计:
asset_create_operation
asset_update_operation
asset_issue_operation
asset_reserve_operation
asset_claim_fees_operation
override_transfer_operation
proposal_delete_operation
account_whitelist_operation
修复建议:为上述 operation添加所需权限 get_required_xxx_uid_authorities
该问题在最新代码版本已经修复,为每个 operation添加了所需权限
33
4.2 client 钱包的安全配置问题
RPC公网暴露会导致的安全问题:
本地钱包 client会开启 rpc和 ws接口,但是不建议开放公网访问,公网访问会导致
钱包资产私钥被盗等严重安全问题;官网测试命令中配置例子中存在公网开放的 ws:
unlock接口爆破:
钱包节点 unlock解锁接口没有频次限制,可以被暴力破解:
简单测试:
启动 wallet客户端,使其保持 locked状态:
构造 unlock的 http数据包
然后在密码字段添加 payload字典.并在字典的最后放置正确的密码作为验证
34
执行爆破, 查看结果
可以看到成功解锁客户端返回的数据.并且 wallet客户端回车就可以看到 unlock状态.
这些都可能给钱包 client带来安全隐患
35
安全建议:
1. Client 程序内设置 rpc 和 ws ip 不允许设置为 0.0.0.0,防止公网暴露,配置文档中
提示相应安全风险
2. 钱包 unlock接口加上频率限制,unlock设置缓存时间,一定时间后自动 lock
3. 钱包私钥管理类 api建议放到命令行操作,不暴露到 rpc 和 ws,防止接口泄露私
钥的风险
4. 钱包密码增加密码复杂度检测,禁止简单密码
4.3 代码静态扫描及 fuzz 测试结果
A. 静态扫描
c++静态安全扫描后通过分析,排除了绝大部分误报,剩下三个确定的 bug。
详细信息如下:
1. 文件位置:fc\src\crypto\elliptic_common.cpp
代码行:238
名称:SizeofForPointerSize
严重级别:Warning
36
描述:Use sizeof data of 'buffer' instead of sizeof the pointer.
buffer的长度这里不能用 sizeof来获取
修复建议:去掉 sizeof,buffer的长度可以通过一个整型变量表示。比如:
buff_len = key.size() +4;
2. 文件位置: fc\src\crypto\elliptic_secp256k1.cpp
代码行:196
名称:SizeofForPointerSize
严重级别:Warning
描述:Use sizeof data of 'buffer' instead of sizeof the pointer.
37
bug造成的原因同第一个,修复建议与第一个相同。
3. 文 件 位 置 : fc\vendor\websocketpp\websocketpp\transport\asio\connection.hpp
代码行:706
名称:dereferenceIfNull
严重级别:Critical
描述:[m_proxy_data] is null dereferenced here, as codes at line 703 make it a null pointer.
在 703行判断m_proxy_data为空,才会执行 706行m_proxy_data->timer->cancel(),空指针访问
bug。
修复建议:去掉这一行,修改判断逻辑。如果没有调用到这个函数,则无需修改。
38
B. 动态 fuzz结果调试分析汇总
1. 文件位置:yoyow-core/libraries/fc/src/network/http/http_connection.cpp
行数:153
类别: bug
错误原因: 指针 end访问内存越界,while判断逻辑有问题,由于不清楚 end的边界,导致了内存访
问违规
修复建议:首先确定网络报文的长度,然后根据长度去读报文。
39
payload名:SIGSEGV.PC.d824848b48.STACK.badbad18f612ef96.CODE.1.ADDR.0xd824848b48.INSTR.[NOTMMAPED].2019-04-26.1625_32.97057.fuzz
导致的问题: 可能导致 client崩溃