IPFS详解
Kademlia DHT
Kademlia DHT是一种分布式哈希算法。
假设现在所有的文件被分布式存储在不同的机器上,如果我们想要查询a的位置,应该去哪个机器上查询?
一种思路就是所有的节点都存储所有文件存放的位置。但这样的弊端是显而易见的:
一方面如果有一个新的文件加入到系统中,那么需要把它的位置广播给所有的节点,开销很大。另一方面,一但有一个节点被攻破,那么攻击者就可以的得知所有的文件存储的位置,不安全。
Kademlia DHT的思想很巧妙。首先每个存储节点都有一个NodeId(160位2进制),如果文件a的hash计算出来为10000100(这里简写了,实际肯定比这复杂),那么要求a文件存储在NodeId最接近hash的k个节点处比如 10000100,1000101......
那么如何找到这些节点的路由呢?
Kademlia DHT要求每个节点存储部分节点的路由信息,同时按照节点hash的异或值分层存储,换句话说,就是按与自己的距离分层存储,假设该节点的id为00000100那么 他会把存储的节点NodeId前160位与自己相同的放在一层,前159位与自己相同的放在一层......所以每个节点都维护了一个160层的路由信息。
现在它收到一个查询请求,查询hash为1000100的文件在哪,先看看是不是在自己这,发现没有,计算距离为00000100^10000100=10000000,也就是说前8层不同,那他就去第八层随机找一个节点,把查询请求发送给他,以此类推,每次查询都是将距离减半
查询平均o(logN)的时间复杂度
Coral DSHT
DSHT是基于DHT,只不过它又将节点按照往返时延分为L0,L1,L2三层,L2的往返时延最低,L0最高,在进行DHT查找时,会优先查找L2的节点,找不到再往下查找L1,L0的节点,DSHT算法的时间复杂度与DHT相同,但实际运行效率却大大提高,但是节点加入/删除时的系统维护成本较高
S/Kademlia DHT
S/Kademlia DHT主要是解决DHT的安全问题
DHT安全问题
按照受到攻击的结构来看,攻击主要分为两类,第1类攻击是针对路由表控制网络中部分节点;第2类则是恶意消耗占用节点的资源。前者包括日蚀攻击、女巫攻击、流失攻击(Churn attack)和对抗路由攻击。
(1)日蚀攻击
如果一个节点在网络中能够自由选择它的ID,如果被攻击者某个K桶不满,或者有节点死掉了,那么攻击者就可以进入自己想攻击节点的K桶中
(2)女巫攻击
在开放的对等网络里,攻击者可以假冒多个ID,用少数网络节点控制多个虚假身份。KAD网络难以控制节点的数量,那么攻击者伪造大量虚假节点身份,就能控制部分网络。通常情况下可以通过消耗一定的系统和计算资源提高女巫攻击者的成本。当然,这也只能改善并不能杜绝。
(3)流失攻击
攻击者拥有网络的一些节点,即恶意节点,这可能会在网络中引发大量流量流失,从而导致网络稳定性降低。
(4)对抗路由攻击
恶意节点在收到查询指令后,不是按照KAD的要求返回距离Key最接近的网络节点,而是转移给同伙节点。同伙节点也做同样的操作,而不返回给查询节点所需要的信息,那么这样一来查询就会失效。我们发现,整个过程中必须将查询信息传递给恶意节点,这一攻击才能发动。那么我们可以在查询时,设计算法并行地查询,并且每一条查询路径不相交。这样一来,只要并行查询的路径中有一条不碰到恶意节点,查询就能成功了。
S/Kademlia DHT
S/K协议就是做出了上述的几个改进:为了避免日蚀攻击和女巫攻击,S/K需要节点不能自由选择节点ID,不能大批量生成ID,同时不能窃取和伪装其他节点的ID。这一方法可以通过非对称加密确保节点身份不被窃取,我们可以设置一定的计算量障碍,强迫节点进行一定的哈希运算来确保不能自由选择和批量生产ID。
为了避免对抗路由攻击,我们需要并行查找不相交的路径。
(1)安全的节点分配策略
S/K节点ID分配策略方案有3个要求:节点不能自由选择其ID;不能生成多个ID;不能伪装和窃取其他节点的ID。
为了实现这些要求,S/K设置了如下方法增加攻击的难度。每个节点在接入前必须解决两个密码学问题,静态问题是:产生一对公钥和私钥,并且将公钥做两次哈希运算后,具有c1个前导零。那么公钥的一次哈希值,就是这个节点的NodeID。动态问题是:不断生成一个随机数X,将X与NodeID求XOR后再求哈希,哈希值要求有c2个前导零。静态问题保证节点不再能自由选择节点ID了,而动态问题则提高了大量生成ID的成本。那么女巫攻击和日蚀攻击将难以进行。
为确保节点身份不被窃取,节点需要对发出的消息进行签名。考虑安全性,可以选择只对IP地址和端口进行弱签名;或者对整个消息进行签名,以保证消息的完整性。在其他节点接收到消息时,首先验证签名的合法性,然后检查节点ID是否满足上述两个难题的要求。我们发现,对于网络其他节点验证信息的合法性,它的时间复杂度仅有(1);但是对于攻击者,为了生成这样一个合法的攻击信息,其时间复杂度是(2c1+2c2)。合理选取c1和c2,就能有效避免这3种攻击方式了。
(2)不相交路径查找算法
在KAD协议中,我们进行一次查询时,会访问节点中的个K-Bucket中的节点,这个K-Bucket是距离我们需要查询的Key最近的。收到回复后,我们再进一步对返回的节点信息排序,选择前个节点继续迭代进行请求。很明显,这样做的缺点是,一旦返回的其他节点信息是一组恶意节点,那么这个查询很可能就会失败了。
为解决这个问题,S/K提出的方案如下:每次查询选择k个节点,放入d个不同的Bucket中。这d个Bucket并行查找,Bucket内部查找方式和KAD协议完全相同。这样一来,d条查找路径就能做到不相交。对于任意一个Bucket,有失效的可能,但是只要d个Bucket中有一条查询到了所需要的信息,这个过程就完成了。
通过不相交路径查找,能解决对抗路由攻击。S/K协议将Kademlia协议改进后,针对常见的攻击,其安全性能大大提高了。
BitTorrent
BitTorrent 协议是架构于 TCP/IP 协议之上的一个 P2P 文件传输协议,处于 TCP/IP 结构的应用层。BitTorrent 协议本身也包含了很多具体的内容协议和扩展协议,并在不断扩充中。如果有多个下载者并发的下载同一个文件,则每个下载者也同时为其它下载者上传文件,这样,文件源可以支持大量的用户进行下载,而只带来适当的负载的增长。
BitTorrent 协议把提供下载的文件虚拟分成大小相等的块,块大小必须为 2k 的整数次方(由于是虚拟分块,硬盘上并不产生各个块文件),并把每个块的索引信息和 Hash 验证码写入 .torrent 文件(即种子文件,也简称为“种子”)中,作为被下载文件的“索引”。 下载者要下载文件内容,需要先得到相应的 .torrent 文件,然后使用 BT 客户端软件进行下载。
下载时,BT 客户端首先解析 .torrent 文件得到 Tracker 地址,然后连接 Tracker 服务器。Tracker 服务器回应下载者的请求,提供下载者其他下载者(包括发布者)的 IP。或者,BT客户端也可解析 .torrent 文件得到 nodes 路由表,然后连接路由表中的有效节点,由网络节点提供下载者其他下载者的 IP。
下载者再连接其他下载者,根据 .torrent 文件,两者分别对方告知自己已经有的块,然后交换对方没有的数据。此时不需要其他服务器参或者其他网络节点的参与,分散了单个线路上的数据流量,因此减轻了服务器负担。下载者每得到一个块,需要算出下载块的 Hash 验证码与 .torrent 文件中的对比,
如果一样则说明块正确,不一样则需要重新下载这个块。
因此,下载的人越多,提供的带宽也越多,种子也会越来越多,下载速度就越快。
IPFS
IPFS是以上技术的融合。
介绍点不同的。
IPFS与BitTorrent相似也是每个文件被分为不同的块,但与BitTorrent不同的是,节点间不止交换自己需要的块,也就是说每个节点不止下载自己需要的文件的块,也会下载别的文件的块,节点优先下载自己需要的块,然后是其他节点需要的块,这也就鼓励每个节点缓存比较稀有的块。
同时在IPFS中有一个“信用分”机制。如果一个节点只下载不分享,那么它的信用分就很低,节点在建立连接时,会优先与信用分高的节点建立连接。
BitSwap节点会记录下来和其他节点通信的账单(数据收发),可以保持节点间数据交换的历史和防止篡改。当两个节点之间建立连接的时候,BitSwap会相互交换账单信息,如果账单不匹配,则清除重新记账。恶意节点可能会故意“丢失”账单,以希望清除掉自己的债务。其它交互节点会把这些都记下来,如果总是发生,节点就会被拒绝。
每个文件在IPFS都是通过Mekle DAG(可以简单理解成Mekle tree,但没有树的那么多限制)存储的,这也就保证了文件的任意一部分的不可篡改性。同时Mekle DAG可以很方便的实现版本控制,由于文件在Mekle DAG中是分Mekle节点存储的,如果文件某个部分改变了,我们只需要复制并且更新该Mekle节点以及上层节点即可。
同时允许每个父节点保存子节点的索引,那么我们只需要给定文件的索引(Mekle DAG的根哈希),就很容易拼接出整个文件
SFS命名方案:每个节点的NodeId=hash(public_key),同时每个节点有一个命名空间/ipns/