00 前言
居善地,心善渊,与善仁,言善信,政善治,事善能,动善时。
01 背景
日蚀攻击(Eclipse Attack)是一种针对点对点网络(p2p)特殊拓扑结构的攻击手段。大家都知道日蚀,一种天体物理现象,月球在白天的时候运行到地球与太阳之间,遮住太阳光,形成日蚀。日蚀攻击取其意相,在网络种,通过恶意网络连接将目标节点从网络链接环境中隔离开来,使目标节点无法获取网络上的正常数据,似日蚀下地球无法获取太阳光,故名日蚀攻击。针对传统P2P网络的日蚀攻击由来已久,但是首个分析针对区块链网络日蚀攻击的手段可行性和可能造成的严重后果进行分析验证的论文首见于2015年的信息安全顶级会议USNIX,由波士顿大学的 Ethan Heilman 在其论文 《Eclipse Attacks on Bitcoin’s Peer-to-Peer Network》中对这种攻击手段进行了介绍。
我们知道在比特币网络中目前理论上存在的几乎唯一的攻击手段是51%的算力攻击,虽然这种攻击手段在别的基于PoW共识算法中已经被成功实施了很多次,比如以太经典,但是在比特币网络里却从来没有任何被人成功实施过的迹象,毕竟花费数亿美元的代价去实施攻击无论怎么看都是得不偿失的。但是在日蚀攻击手段下,攻击者可以不对整个网络进行攻击,而是针对特定的节点,比如交易所,来进行攻击,这样的攻击成本将远小于51%的算力需求。
在实施日蚀攻击的时候,攻击者可以向受害者发起大量的建立网络连接的请求,使自己成为目标节点的Peer node,通过这样的手段,一旦受害者所有的peer node都变成由攻击者控制的节点时,受害者在区块链网络的所有input和output都将收到攻击者的监视和控制。
一旦控制了受害者的所有区块链网路通信,攻击者就可以拦截正常区块链网络里同步的交易,新区块等信息,转而向受害者广播自己挖掘的新区块,而由于受害者无法获取正常网络的区块信息,攻击者将不再受限于攻击正常网络所需的51%算力,而只需要掌握比受害者多的算力即可完成攻击过程。
日蚀攻击针对普通的节点可以使目标节点的算力无效,挖出无效的区块,相应的使自己的算力在区块链网络中占比增加。而针对交易所的日蚀攻击则可能通过一个单独对交易所发起的转账交易或者提款交易而在交易所节点中完成双花攻击或者直接窃取交易所持有的加密货币。
02 来自peer node的网络封锁
以我的了解来看,想发起日蚀攻击,那么攻击者必须用自己控制的节点完全取代受害者在区块链网络中的正常peer node,否则任何一个可能的链接都会使得受害者接收到正常的区块和交易。此外,攻击者应该有能力构建伪造的新区块并且可以通过受害者节点的验证,否则,仅仅是将目标节点从区块链网络中隔离开来,攻击者所能获得的收益将无法最大化。基于以上,我们来分析对NEO网络发起日蚀攻击的可能性。
在这里我们先分析将NEO节点在网络上进行封锁的可行性。网络部分想了解详细内容的移步《NEO从源码分析看网络通信》。
获取网络节点的方式除了从NEO服务器获取之外还有一个主动获取的方式,那就是向所有的与本地节点建立连接的节点广播网络节点请求,通过获取这些与远程节点建立连接的节点列表来实时获取整个网络中的节点信息。 --- from NEO从源码分析看网络通信
通过对NEO网络机制的分析,我们可以知道,至少NEO在网络通信这块我们是可以直接跟目标节点建立通信的,一个好的开头,然后我我们再分析其都有哪些限制,可不可能通过单一节点来发起大量的网络连接请求阻塞目标节点。
通过分析源码,我们在src.neo.Network.P2P.ChannelsConfig.cs 里看到了NEO针对peer节点ip的限制,同时也还有针对最大链接数量的限制,这是好事,至少意味着隔绝节点的工作量是有上限的。
/// <summary>
/// Max allowed connections
/// </summary>
public int MaxConnections { get; set; } = Peer.DefaultMaxConnections;
/// <summary>
/// Max allowed connections per address
/// </summary>
public int MaxConnectionsPerAddress { get; set; } = 3;
但是对于同一ip的链接限制也意味着没办法通过简单从一台机器发出大量链接请求来实施廉价高效的攻击。从攻击者角度来说这很不友好。
更不友好的是,NEO在初始化的时候,会先加载一个seedlist作为初始peer nodes的候选人。
源码位置:src.neo.Network.P2P.LocalNode.cs
private readonly IPEndPoint\[\] SeedList = new IPEndPoint\[ProtocolSettings.Default.SeedList.Length\];
而这个SeedList里明显不会有攻击者的地址:
"seed1.neo.org:10333",
"seed2.neo.org:10333",
"seed3.neo.org:10333",
"seed4.neo.org:10333",
"seed5.neo.org:10333"
这里就会出现麻烦,因为我们没办法在不直接攻击目标节点或者DNS的前提下让目标节点和这些seedlist断开链接。
03 从共识入手
我们再来分析NEO的共识机制。更详细的内容可以移步我的《NEO从源码分析看共识协议》。
NEO采用得失DBTF共识机制,国产共识,值得拥有。在DBTF共识过程中,并不是所有的节点都有参与共识的资格,而是需要投票选举出一票议员(目前还是官方指定)。这样一票议员的列表在每一个节点内都有备份,所以想要把自己伪造成议员是不太现实的。在共识的过程中,新区块由议员轮流化身议长主持生成,然后由超过2/3的议员验证签名确认后生效。在普通节点同步新区块的时候,是会验证区块的签名,如果签名数据不合法,那么新区块就会被无情拒绝。
DBTF的这种共识结构对日蚀攻击本身是不友好的,当然,你也可以说是更安全的。因为议长生成区块议员进行验证的机制本身就意味着,即使我们成功把目标节点隔离开,那么也没办法生成合法的新区块,因为攻击者本身不是议员,没机会生成新区块。而且即使攻击者是议员,又假设刚好是议长,这时候情况会稍微乐观点,但是想要往下进展依然很难。
04 来自恶意议员的协助
我们假设议员里有刚好 1/3 - 1 个议员统一协助我们发起攻击,而且我们也不管NEO对peer节点的一系列限制,不要问为什么。
这时候情况似乎一片大好,我们拥有了这么多的卧底,哪怕我们再拉一个人加入我们,我们就可以光明正大的给NEO使绊子了,但我们偏不,我们就要这种刚刚好的感觉。但是真的前景光明么?
由于每个共识周期的区块其实是在共识周期内的视图里生成的,所以我们可以在共识周期里重置视图的地方看到,对于新生成区块其实是由一个多签账户来生成的,这个多签账户就是通过全体议员中超过2/3议员签名来验证通过。
源码地址:src.neo.ledger.Blockchain.cs
public static UInt160 GetConsensusAddress(ECPoint\[\] validators)
{
return Contract.CreateMultiSigRedeemScript(validators.Length - (validators.Length - 1) / 3, validators).ToScriptHash();
}
由于我们的议员数量无法达成生成新的区块的必要条件,所以除了已有的卧底之外,我们还需要添加新的议员进入我们的阵营。但是获取通过“合法”手段获取新的节点似乎是一件不可能完成的任务。首先,在NEO网络中添加新的议员是需要选举过程的,这个过程中所有的交易都需要经过已有的超过2/3的议员的验证才能通过,也就是说,除非我们真的在可以“光明正大”攻击NEO之前先光明正大的通过选举增加攻击者的实力,否则,对NEO网络实施日蚀攻击似乎都是不现实的。
05 总结
本文通过NEO源码的角度一方面分析了日蚀攻击的攻击手段和可能造成的后果,另一方面也探索了面对日蚀攻击的时候NEO系统本身的鲁棒性。本文属于区块链安全科普性文章,部分内容可能不符合专业性,严谨性,酌情饮用。作者实力所限,文中难免疏漏,望不吝交流指点。