易受攻击的智能合约具有各种技术弱点,攻击者可以利用这些弱点窃取资金或数据。
像以太坊这样的区块链依赖于 Turing-complete的语言,比如 Solidity,可以用来编程几乎任何可想象的应用。然而,当你可以编程出任何结果时,预测可能发生的所有意外后果可能是不可能的。例如,一个智能合约可能被编程在某些条件下分发代币。如果一个恶意用户设法找到一种方法使合约在开发者未预见的条件下分发代币,这被视为一个漏洞。
另外,区块链的公开性质引入了攻击者可以利用的某些因素。例如,在交易被确认并录入区块链账本之前,它作为一个更大的未处理交易池的一部分被广播到网络中,这个未处理的交易池被称为 mempool。如何从 mempool 中选择交易以纳入下一个区块也可能受到操纵和游戏化,因为矿工或验证者通常不需要按时间顺序选择交易。
因此,虽然脆弱的代码经常是攻击的根源,但攻击者可能会利用各种因素发起成功的攻击。这类攻击的目标通常是窃取资金或数据,尽管偶尔,攻击者可能是白帽黑客,他们演示了漏洞,并且会在交换漏洞赏金的情况下返还资金。然而,这样的攻击可能仍会削弱用户之间的信任。
作为最佳实践,开发者通常会进行代码审计以识别漏洞和错误。通常,一个程序即使有一些错误也能有效运行;因此,代码审计将根据它们的严重性和风险对错误和漏洞进行分类。
虽然攻击者可能有很多方式来利用智能合约,但有一些已知的漏洞和攻击方法。以下是一些最常见的。
智能合约可能在正常操作中与其他智能合约通信。这种能力是区块链应用可组合性的基础,允许两个或更多的以太坊 DApps 即使在同一以太坊交易中也能相互交互。
重入攻击通过要求代码对不受信任的第二个智能合约进行外部调用(通常是转移资金)来利用智能合约中的漏洞。在交易完成并录入账本之前,重入攻击会进行相同的调用。由于资金更新尚未发生,重入攻击可以持续提款直到合约中的所有资金被耗尽。
最著名的重入攻击之一是 2016 年发生的 DAO 事件,这是一个投资智能合约,被窃取了价值 6000 万美元的 ETH。
编程语言通常不支持浮点数运算,这意味着不能使用小数点来表示数字的分数部分。因此,开发人员在编码应用程序时必须使用整数(整数)。大多数加密货币运作的是加密货币的分数单位,比如 gwei,而不是以太,这有助于将代币值编码为整数。
整数的值不能无限大;它受到 Solidity 字长允许的最大值的限制,即 256 位。在以太坊中,这相当于 43 亿以太。
当智能合约在计算一个值时,如果无符号整数的值最终为零,它将返回 43 亿以太的最大可能值。
攻击者试图通过使用一个恶意地址来利用这一限制,智能合约可以将该地址解读为零余额,然后发送一单位(任何单位)的以太。这迫使智能合约更新到允许的最大值 43 亿以太。
此外,完全用整数生成计算的严格算术逻辑可能会让开发人员陷入困境。例如,百分比计算可以用两种方法进行。如果要计算 90 的 50%,可以这样计算:
90*50 = 450
450/100 = 4
这种方法只使用整数。然而,达到同样结果的另一种方法可以是:
90/100 = 0.9
0.9*50 = 45
这种方法生成了一个无效的输入(0.9),这将创建一个整数错误,可以被利用。
后一个问题通常已经由 Solidity 编译器解决,这些编译器会扫描这些错误,以便可以纠正。然而,2018 年受到整数溢出攻击的以太坊项目 BeautyChain,对于这样的简单修复来说还为时过早。
在以太坊中,每个区块都有 Gas 上限,用于管理新数据添加到区块链的速率。然而,这为数据密集型应用程序带来了挑战。应用程序可能会将数据存储在数组中,然后通过循环这些数组来访问它们。这种活动最终可能意味着交易超出了区块 Gas 上限,并且未能完成。这也可能导致整个合约无效,因为没有进一步的交易能够完成循环。
GovernMental 是一个庞氏加密项目,要求用户向智能合约发送以太。参与者名单变得如此之长,以至于现在区块 Gas 费用上限阻止任何人完成交易,因为需要扫描列表。
智能合约通常有基本的预置条件,例如地址有效或有足够的余额来完成交易等。然而,开发人员经常忽视编码预置条件所需的细节,特别是如果它们很复杂,导致意外错误。
一个例子是 2022 年在 NEAR 上开发的 IDO 平台 Skyward Finance,攻击者利用智能合约没有检查同一个钱包地址不应该被使用多次的事实,攻击者简单地重复提款,总计 320 万美元。
前置交易是另一种攻击类型,它利用了交易在 mempool 中可见的事实。前置交易机器人监控 mempool 中的有利可图的交易。当它们发现一个时,它们简单地复制交易并为其设置更高的 Gas 费。矿工或验证者会被激励选择 Gas 费更高的交易以包含在区块中,最终原始交易将变得多余。
前置交易并不一定是智能合约编码的漏洞,而是区块链整体设计的漏洞。因此,前置交易机器人和攻击相对常见。然而,程序员和应用开发人员可以实施帮助避免前置交易攻击的方法,例如交易计数器,这可能揭示智能合约状态已被修改,或者链下排序系统以确保交易顺序进行。
开发者没有 100% 万无一失的方法来避免所有的漏洞。然而,有一些最佳实践,例如使用最新的语法编写代码,定期更新工具比如编译器,并且在不同的场景下进行广泛而严格的压力测试。
许多项目会为此运行激励性测试阶段,用户通常可以通过参与早期测试程序获得奖励,这有助于开发者发现漏洞和故障。
任何能理解像 Solidity 这样的编程语言的人都可以使用区块浏览器比如 Etherscan 来查看智能合约代码。即使不理解代码,检查区块浏览器数据比如交易历史也可能揭示需要进一步调查的异常情况。例如,缺乏卖出交易可能表明买家被阻止卖出。
避免易受攻击的智能合约的其他方法包括检查项目是否有独立的代码审计,始终使用用户数量多的有信誉的协议,并确保应用和钱包使用可用的最新版本软件。