超过3万枚以太坊已被销毁
以太坊基金会今日发文披露了一个2019年首次发现的安全漏洞,在上个月的柏林升级之前,该漏洞的严重程度为发生攻击时可能使主网瘫痪。该漏洞的本质是触发随机Trie查询,以太坊开发人员曾试图用EIP-1884、EIP-2583、EIP-2929、以及快照功能来抵御该漏洞,最终在柏林升级之后该漏洞危险性降低。
新闻:通过这篇博文,目的是正式披露以太坊平台面临的一个严重威胁。在以太坊柏林硬叉之前,这种威胁是真实存在的。
状态我们先从以太坊和状态的背景知识说起。
以太坊状态由patricia-merkle trie(前缀树)组成。本文不会涉及太多细节。随着状态的增长,这棵树的枝干越来越密。每个添加的帐户都是一片叶子。树根和树叶之间有许多“中间”节点。
为了在这个巨大的树中找到给定的帐户或“叶子”,需要从根到中间节点解析大约6-9个哈希,以便最终解析最后一个哈希,该哈希将指向我们要寻找的数据。
简而言之:每次执行Trie查找来查找帐户时,都会执行8-9次解析操作。每一次解析操作都是一次数据库查找,每一次word数据库查找都可以是任意数量的实际磁盘操作。很难估计磁盘操作的次数,但是由于trie密钥是加密哈希(防冲突),所以密钥是“随机的”,这对于任何数据库来说都是最坏的情况。
随着以太坊的成长,接入trie需要提高气价。这是2016年10月在块高2,463,000的橘哨里表演的,包括EIP150。EIP150在所谓的“上海攻击”后大幅度增加了一些操作的气体成本,并做了一系列的改动来防止DoS攻击。
伊斯坦布尔升级也进行了另一次气升级,即2019年12月块高906.9万。在这次升级中,EIP 1884被激活。
EIP 1884引入了以下运营成本变化:
SLOAD从200气体增加到800气体,
平衡从400气体增加到700气体(自动平衡减少),
EXTCODEHASH从700gas升级到700gas。
问题
2019年3月,马丁·史文德对EVM操作码的性能进行了一些测量。这项调查导致了EIP-1884的创建。EIP-1884上线前几个月,论文《破米》正式发表(2019年9月)。
两名以太坊安全研究人员(Hubert Ritzdorf和Matthias Egli)与论文作者丹尼尔·佩雷斯合作,将一个漏洞“武器化”,他们将该漏洞提交给以太坊赏金计划。这是2019年10月4日。
我们建议您完整阅读该提交的内容。这是一份写得很好的报告。
在专门讨论跨客户端安全的频道上,同一天,来自Geth、Parity和Aleth的开发者被告知关于提交的信息。
该漏洞的本质是触发随机trie查询。一个非常简单的变体是:
在他们的报告中,研究人员通过eth_call在与主网络同步的节点上执行这一有效载荷。以下是使用10M气体时执行的数字:
使用EXTCODEHASH (400气)发动10M气攻击。
平价:~90
大约70岁
使用EXTCODESIZE (700gas)发动10M毒气攻击。
平价:~50
Geth : ~38
显然,EIP 1884引入的变化确实对减少攻击有影响,但它们还远远不够。
在大阪举行敌无双会议之前,情况确实如此。在敌无双期间,主要的网络客户端开发者分享了这个问题的知识。我们还见到了Hubert、Mathias和Greg Markou(来自Chainsafe的ETC工作人员)。ETC开发者也收到了这份报告。
随着2019年接近尾声,我们知道我们遇到的问题比我们之前预期的要大,恶意交易可能会导致阻塞时间间隔增加到分钟。更糟糕的是,开发商一直对EIP-1884不满,因为EIP-1884中断了一些合同程序,用户和矿工都急于提高气体限制。
此外,仅两个月后,2019年12月,平价以太坊宣布退出以太坊,而OpenEthereum则接管了代码库的维护工作。
之后,创建一个新的客户端协调通道。在这个渠道中,Geth、Nethermind、OpenEthereum和Besu开发者继续协调。
解决方案
我们意识到我们必须采取两种方法来解决这些问题。一种方式是使用以太坊协议,用某种方式解决协议层的问题。最好不要毁约,最好不要惩罚“好”的行为,但还是要尽量防止攻击。
第二种方法是通过软件工程,改变客户端的数据模型和结构。
协议层工作
如何应对这些类型的攻击的第一次迭代升级可以在这里查看。2020年2月,该解决方案以EIP 2583的形式正式发布。其背后的想法是,每当Trie搜索导致失败时,简单地增加罚款。
但是,彼得找到了解决这一想法的方法——“阻挡接力”攻击——并对这一惩罚的有效范围设定了上限(约800gas)。
漏判造成的罚分问题是,要确定必须罚分,必须先查。但如果剩余的气不足以罚款,则说明未交费已经执行。即使它确实导致抛出异常,您也可以将这些状态读取包装到嵌套调用中。允许外部呼叫者继续重复攻击而不支付(全部)罚款。
因此,这个EIP被放弃了,我们正在寻找一个更好的替代方案。
阿列克谢·阿克洪诺夫(Alexey Akhunov)探讨了石油的概念,石油是“气”的第二个来源,但它与气有本质区别,因为执行层看不到它,它可能导致交易的全球减少。
马丁在2020年5月提出了类似的建议,关于因果报应。
在迭代这些计划时,Vitalik Buterin建议只增加天然气成本和维护访问列表。2020年8月,马丁和维塔利克开始迭代,成为EIP-2929和EIP-2930。
EIP-2929有效地解决了许多以前的问题。
与EIP-1884(无条件成本增加)相反,它只增加了尚未访问的内容的成本。这导致净成本增加不到1%。
此外,像EIP-2930,它不会破坏任何合同流彩币钱包同步。
此外,还可以通过增加燃气成本(不中断运行)来进一步调整。
2021年4月15日,都随着柏林的升级上线了。
开发工作
2019年10月,彼得试图通过拍摄动态快照来解决这个问题。
快照是一种二级数据结构,用于以平面格式存储以太坊的状态。它可以在Geth节点实时运行期间完全在线构建。
根据优点是,它充当状态访问的加速结构:
快照不需要提供O(log N)磁盘读取(x LevelDB开销)来访问帐户/存储槽,但可以提供直接O(1)访问时间(x LevelDB开销)。
快照支持每个条目复杂度为O(1)的帐户和存储迭代,这使得远程节点检索顺序状态数据比以前便宜得多。
快照的存在还支持更特殊的用例,例如离线修整状态Trie或迁移到其他数据格式。
快照的缺点是原始帐户和存储的数据实际上是重复的。对于主网络,这意味着使用额外的25GB SSD空空间。
动态快照的想法从2019年年中就开始了,主要目的是成为快照同步的推动者。当时,Geth团队正在进行许多“大项目”。
离线修剪
动态快照+快照同步
按碎裂状态的LES状态分布
不过最后还是决定全面优先快照,暂时推迟其他项目。这些为后来的snap/1同步算法奠定了基础。2020年3月并入主网。
随着“动态快照”功能的发布,我们有了一些喘息的空间空。如果以太坊网络被攻击,会很痛苦,是的,但至少可以通知用户启用快照。整个快照生成会花费很多时间,快照还不能同步,但是网络至少可以继续运行。
从2021年3月到4月,在geth中引入了snap/1协议,以便可以使用新的基于快照的算法进行同步。虽然它仍然不是默认的同步模式,但这是为了使快照不仅用作攻击保护,而且对用户来说也是一个显著的改进。
协议方面,柏林升级于2021年4月正式实施。
以下是我们在AWS监控环境中开发的一些基准:
在柏林升级之前,没有快照,25米汽油:14.3
升级柏林之前有个快照,25M气:1.5
升级柏林后,没有快照,25m气:~ 3.1
升级柏林后有快照,25m气:~ 0.3
(粗略)数字显示,柏林升级降低攻击效率5倍,快照降低攻击效率10倍,总共降低攻击影响50倍。
我们估计,目前在主网(15M gas)上,在没有快照的情况下,在一个geth节点上创建一个块可能需要2.5-3s。随着状态的增长,这个数字将继续恶化(对于非快照节点)。
如果您使用退款来增加一个区块的有效用气量,则可以进一步增加(最大)2倍。有了EIP 1559,块气体限制将更加灵活,它将被允许在临时突发中增加2倍(弹性_乘数)。
至于实施这种攻击的可行性;攻击者购买一个完整区块的成本大约是几个ETH(100 gwei的价格1.5ETH换15个gas)。
为什么现在披露这个威胁?
长期以来,这种威胁已经是“公开的秘密”。事实上,至少有一次无意中公开披露过,在ACD电话会议上也多次提及,但都没有明确的细节。
由于柏林升级现在已经过去,geth节点默认使用快照,我们估计这种威胁的危险性很低,可以公开。现在是时候全面公开之前开发者的幕后工作了。
至关重要的是,社区必须有机会了解对用户体验有负面影响的变化背后的原因,如增加汽油成本和限制退款。
本文由马丁·霍尔斯特·斯温德和彼得·西拉吉于2021年4月23日撰写。2021年4月26日与其他基于以太坊的项目共享,2021年5月18日公开披露。