Hyperledger Fabric的安全与隐私保护机制
通过Fabric组网并运行比较简单,但是如何根据业务需求,设计联盟,分配组织节点,配置操作访问策略,保护隐私数据是Fabric的难点,本文通过以下四个方面介绍Fabric的安全与隐私保护机制。
1:依靠联盟完成系统隔离
联盟是Fabric网络中的最高隔离,通过组织、节点、用户与数字证书的绑定完成系统隔离。联盟链又叫做许可链,首先要解决的问题是接入者的身份问题,Fabric根据PKI体系生成联盟内的一组证书文件。通过CA颁发组织的证书,通过组织证书来签发子证书(节点证书、用户证书),构成证书链的结构。
联盟内各节点都需要同步各组织的MSP成员关系证书,这样A组织的节点就可以通过B组织的证书来验证对方的身份。Fabric 设计中考虑了两种类型的证书:登记证书和TLS 证书。主要通过 ECert 对实体身份进行检验,通过检查签名来实现权限管理。证书的默认签名算法为 ECDSA,Hash 算法为 SHA-256。
- 登记证书(ECert):颁发给提供了注册凭证的用户或节点等实体,代表网络中身份。
- 通信证书(TLSCert):控制对网络层的接入访问,可以对远端实体身份进行校验,防止窃听。
证书颁发又分为:
- 组织证书,颁发给联盟链中的组织(Org)
- 节点证书,颁发给组织中的节点(Peer、Orderer)
- 账户证书,颁发给组织中的用户(User、Admin)
2:引入多账本实现数据隔离
Fabric中存在通道的概念,通道用于向联盟中的组织间提供私有通信机制,每一个通道对应一个账本,通过通道来实现数据隔离。举例说明,通道类似于微信聊天群的概念,不同的好友加入同一个群,那么聊天记录就是群组同步共享的,非群成员是看不到群消息的。聊天消息按照时间顺序排列,类似于区块链中链式结构的账本。
Fabric通过通道配置文件configtx.yaml配置文件来创建群组,将组织中的节点加入到通道中。通道内的数据理论上所有节点是共享的,数据访问的权限由策略控制。
3:依赖账本配置完成应用隔离
通过账本的权限配置来完成应用隔离,因为账本的数据并不一定让所有的成员都可见。策略定义了Fabric系统操作和数据访问的权限,访问策略在configtxgen模块的配置文件configtx.yaml。策略控制的含义可以简单理解为身份属于什么角色、并具备什么权限。
- 身份:包括Peer节点、Orderer节点、Admin用户、User用户。
- 角色:角色根据证书的类型来划分。
Admin具体指Admin用户
Client具体指User用户
Peer具体指Peer节点
Orderer具体值Orderer节点
Member指以上所有的角色
策略类型:
SIGNATURE策略,指定通道签名来对数据进行认证,比如必须满足给定身份的签名组合。
IMPLICIT_META策略,通过引用其他子策略(最终还是SIGNATURE策略)来实现。
通道策略
权限策略的主要应用场景就是通道策略,通道策略采用了层级化树型结构,最上层是Channel组,每个级别都可以指定策略,作为本级别的默认策略。
例如上图:需要向联盟中增加新的组织,需要满足Channel/Admins策略,MAJORITY sub policy:“Admins”的策略表明需要满足半数子策略,也就是Channel/Applicaiton/Admins和Channel/Orderer/Admins两个子策略中的任意一个。追溯到最后一层的子策略就是各个组织管理员角色的签名。
4:通过智能合约完成操作隔离
智能合约对权限的管控可以分为三个方面:
- 通过实例化链码时指定链码背书策略
- 通过指定隐私数据集合实现隐私保护
- 通过设计数据与权限的链码实现访问控制
链码背书策略
用户在实例化链码时,可以指定调用链码所需要满足的背书策略,当对链码的调用交易被提交时,Peer会检查是否交易携带了符合指定背书策略的签名信息。如果不设置背书策略,默认为通道中内大多数成员。
identities:
user1: {"role": {"name": "admin", "mspId": "Org1MSP"}}
user2: {"role": {"name": "admin", "mspId": "Org2MSP"}}
policy:
# n-of 指定需要组内多少个进行签名, 1-of 等价于 OR, max-of 等价于AND
1-of:
- 1-of:
- signed-by: "user1"
- 1-of:
- signed-by: "user2"
该策略文件表示链码执行必须由Org1的管理员或者Org2的管理员进行签名。
背书策略除了链码背书策略,还有键值背书策略(为每个键值指定背书策略)、私密数据集合背书策略(为每个秘密数据集合指定背书策略)。
隐私数据库的实现
如果通道上的某些组织想要保持数据的隐私,对通道上的其它组织保密,那么最直接的做法是创建一个新的通道,只让具有隐私数据访问权的组织加入,然而建立单独的通道会产生额外的管理开销(维护链码版本,背书策略,MSP等。Fabric可以创建私有数据集合,从而使通道上已定义的组织子集能够背书、提交或查询私有数据,而无需创建单独的通道。
什么时候使用私有数据vs单独的通道?
单独的通道:如果整个交易(账本)必须在属于该通道成员的一组组织内保持机密。
私有数据:当必须在一组组织之间共享账本时,但是当这些组织的子集应该可以访问某些(或全部)数据时。此外,由于私有数据是通过点对点而不是通过块进行分发的,当必须对排序节点交易数据保密时,使用私有数据集合。
Peer节点中保存有两种数据,通过文件的形式保存了区块数据,每个区块则保存了交易信息,KV数据库中保存有世界状态,世界状态中一部分保存了公共状态,公共状态保存了节点共享的数据信息。另一部分就是隐私数据集合,当该Peer节点被设置成无权限访问私有数据时,节点只会保存隐私数据的哈希值,也就是数据的密文。当该节点被允许访问私有数据,数据会以明文的形式保存到隐私数据集合中的隐私数据部分。
定义了两个私有数据集collectionMarbles 和 collectionMarblePrivateDetails。其中,collectionMarbles 集合定义当中的policy 属性允许 org1 和 org2 的成员节点在私有数据库中存储和查询实际私有数据,collectionMarblePrivateDetails 集合只允许 org1 的成员节点存储和查询私有数据。私有数据保护策略会映射到智能合约当中,并通过智能合约访问私有数据。
[
{
"name": "collectionMarbles",
"policy": "OR('Org1MSP.member', 'Org2MSP.member')",
"requiredPeerCount": 0,
"maxPeerCount": 3,
"blockToLive":1000000,
"memberOnlyRead": true
},
{
"name": "collectionMarblePrivateDetails",
"policy": "OR('Org1MSP.member')",
"requiredPeerCount": 0,
"maxPeerCount": 3,
"blockToLive":3,
"memberOnlyRead": true
}
]
将敏感数据比如商品价格,序列化后调用PutPrivateData接口,保存到key值是collectionMarblePrivateDetails的世界状态中,只有Org1有权限访问。
marblePrivateDetails := &marblePrivateDetails{
ObjectType: "marblePrivateDetails",
Name: marbleInput.Name,
Price: marbleInput.Price,
}
marblePrivateDetailsBytes, err := json.Marshal(marblePrivateDetails)
if err != nil {
return shim.Error(err.Error())
}
err = stub.PutPrivateData("collectionMarblePrivateDetails", marbleInput.Name, marblePrivateDetailsBytes)
if err != nil {
return shim.Error(err.Error())
}
链码设计
如果以上权限控制都无法满足业务需求,可以考虑通过设计自带权限控制的链码。目前最常用的就是数据与权限分离,通过更新权限链码来动态修改数据访问权限。
例如存在一种应用场景,A组织和B组织建立了一个通道,已经共享了大量数据,如果这时候新加入了C组织,但又不想让C组织同步A和B已有的数据。Fabric系统的权限控制难以满足业务需要,因为数据是通道内共享的,C组织的加入必定要同步A和B已经产生的区块。
可以通过设置两个通道,分别部署数据链码和权限链码,Fabric跨通道可以具有读权限,组织产生的数据都同步到数据账本,通过更新权限链码为C组织设置数据访问权限,组织在每次访问数据前都要获取权限,这样就实现了数据与权限分离。