一、多群组FISCO BCOS的权限漏洞由来
1.1 什么是多群组场景的权限问题
FISCO BCOS是国内常用的联盟链,很多企业会用它做业务隔离——把不同部门、不同合作伙伴放到不同的“群组”里,就像给每个单位分独立的办公室,防止乱串。但早期的合约设计里,会忽略“调用者属于哪个群组”这个关键信息,导致某一个群组的管理员,竟然能随便操作别的群组的合约,这就是典型的越权漏洞,好比你有A公司的门禁卡,却能随便开B公司的大门。
1.2 实际踩坑的典型表现
举个常见例子:某电商联盟链,有商家群和运营群,商家只该管理自己的店铺库存,运营管全平台的活动。旧的权限合约没校验群组,运营的账户在商家群里登录后,能直接修改任何商家的库存,这就是越权。之前就有开发者遇到过,没修复前测试时,跨群组操作一点就成,完全没阻隔。
二、漏洞修复的核心思路
2.1 修复的核心逻辑
其实很简单:每次有人调用合约时,先查两个信息——第一,当前请求到底是在哪个群组发的;第二,这个请求的人,是不是该群组的合法权限者。把这两个校验加上,就能把不同群组的权限彻底隔开,就像给每个办公室加专属门禁,卡只能开对应门。
2.2 完整的代码修复示例
这里用FISCO BCOS默认支持的Solidity语言写,所有代码都按官方规范来,单一技术栈没有混合。
// 修复后的权限合约,先引入FISCO BCOS自带的群组查询工具
// 这个工具是预写好的,直接用就行,用来拿当前请求的群组ID
import "./precompiled/GROUP_TABLE.sol";
contract GroupPermission {
// 把管理员权限按群组分开存,每个群组对应一组管理员,不会混
// 外层key是群组ID(数字,比如1、2、3),内层是该群组的管理员地址
mapping(uint64 => mapping(address => bool)) public groupAdmins;
// 1. 添加群组管理员的函数,必须指定自己的群组,不然加不进去
function addGroupAdmin(uint64 targetGroup, address newAdmin) public {
// 第一步:查当前这个请求到底属于哪个群组,和要加的目标群组对比
uint64 currentCallGroup = GROUP_TABLE.getCallerGroupID();
require(currentCallGroup == targetGroup, "只能给当前群组加管理员,不能跨群操作");
// 第二步:确保调用者本身是当前群组的管理员,不然没资格加人
require(groupAdmins[currentCallGroup][msg.sender], "你不是本群组管理员,无权添加");
// 把新管理员加入对应群组
groupAdmins[targetGroup][newAdmin] = true;
}
// 2. 修改群组资源的函数,必须同时校验群组和权限
function modifyGroupResource(uint64 targetGroup, string memory newData) public {
// 校验是不是在自己的群组操作
uint64 currentCallGroup = GROUP_TABLE.getCallerGroupID();
require(currentCallGroup == targetGroup, "只能修改自己群组的资源");
// 校验是不是群组管理员
require(groupAdmins[currentCallGroup][msg.sender], "不是群组管理员,无权修改");
// 这里才执行真正的修改逻辑,前面的校验都通过才会往下走
// (实际业务里替换成你要修改的资源代码即可)
}
}
简单解释下:那个GROUP_TABLE.getCallerGroupID()就是FISCO BCOS自带的工具,不用自己写逻辑,就能拿到你现在用哪个群组发的请求,相当于查你手里的卡是哪个群的。
三、修复后的效果与适用场景
3.1 能解决的实际业务场景
适合所有多群组隔离的联盟链场景:比如银行不同分行的群组、企业不同事业部的群组、电商不同品牌商家的群组。比如银行A分行的管理员,只能操作A分行的客户数据,再也不能随便改B分行的,完全符合业务的隔离要求。
3.2 修复方案的优缺点
优点很明确:第一,群隔离彻底,绝无跨群越权的可能;第二,代码逻辑简单,哪怕是刚学Solidity的开发者也能看懂;第三,符合FISCO BCOS官方的多群组设计规范,后续升级也有保障。 缺点只有一个:每次调用合约多了两步群组校验,会稍微增加一点点链上的计算量,但现在的链性能很强,这种损耗几乎可以忽略,对用户来说完全感知不到。
四、修复过程中的注意事项
4.1 不要搞错群组ID的来源
一定要用GROUP_TABLE.getCallerGroupID()这个函数,不能写死群组ID,也不能让前端随便传群组ID。比如前端固定绑死当前页面对应的群组,不能让用户手动选别的群,不然还是会有漏洞。
4.2 旧合约的兼容处理
如果已经有上线的旧合约,直接部署新合约替换就行,同时要把旧的管理员数据迁移到新的结构里——比如把原来“全链管理员”的列表,拆成每个群组的管理员列表,别用全局的权限变量,不然和旧漏洞一样。
4.3 权限配置的同步
每个群组的管理员要单独配置,比如A群组的管理员只能在A群组加人,B群组的只能在B群组操作,绝对不能跨群组共用权限,这是修复后必须严格遵守的规则。
五、总结
多群组场景下的权限越权,本质就是没有把“群组身份”当成权限校验的核心字段,修复的核心就是给每一次合约调用加上群组的“门禁锁”。这个修复方案非常落地,很多做联盟链的开发者之前踩过这个坑,只要按上面的代码改,就能解决大部分跨群权限问题。对于需要稳定多群组隔离的业务来说,这个措施是必须做的基础安全加固。
评论
围绕“多群组场景下FISCO BCOS系统合约调用权限控制越权的修复措施”参与讨论