12种智能合约漏洞及其缓解策略:保障区块链应用安全的核心指南

本文详细解析了智能合约开发中常见的12类安全漏洞,包括重入攻击、预言机操纵、业务逻辑错误等,并结合真实攻击案例提供了具体的修复方案,旨在帮助开发者在部署前识别并消除安全风险,确保合约的可靠性与资产安全。

12种智能合约漏洞及如何缓解它们

智能合约在特定事件发生时自动执行任务,且通常处理大量的数据和资源流。这使它们对攻击者特别具有吸引力。

随着智能合约越来越多地被用于保障数字资产,开发者们面临着一系列安全威胁。近期备受瞩目的攻击事件——包括 Penpie 损失的 2700 万美元和 Cetus 被黑的 2.23 亿美元——凸显了通过正确编码来预防漏洞的重要性。

智能合约根据其编程方式,在满足特定事件、条件和逻辑时执行流程、交易和其他任务。它们部署在区块链上,例如以太坊或其他分布式账本基础设施,在那里监听来自被称为“预言机”的密码学安全数据源的事件和更新。

包括金融、医疗保健和保险在内的许多行业使用智能合约来控制大量有价值数据和资源的流动,例如转移资金、交付服务和解锁受保护的内容。这自然使它们成为恶意行为者有吸引力的目标。

在设计和开发智能合约时,安全必须是首要任务。一旦智能合约部署到区块链上,就很难或不可能打补丁;必须将其移除、重新创建并重新部署。此外,智能合约一旦上链,其漏洞将对任何人开放。

当在流行的智能合约平台以太坊上部署用 Solidity 编写的智能合约时,开发团队需要特别注意以下攻击向量以及如何消除它们。

1. 重入攻击

重入攻击向量存在是因为 Solidity 智能合约是强制性执行的:每一行代码必须在下一行开始前执行完毕。这意味着当一个合约向另一个不同的合约发出外部调用时,调用合约的执行会暂停,直到该调用返回。这实际上给了被调用合约对接下来发生什么的临时控制权,从而创造了无限循环的可能性。

例如,恶意合约可以递归回调原始合约以提取资源,而无需等待第一次调用完成,因此原始合约在函数完成前永远无法更新其余额。重入攻击的形式包括单函数、跨函数、跨合约、跨链和只读攻击。GitHub 维护了一份漏洞利用列表。

修复方法: 当智能合约的代码逻辑存在缺陷时,就会出现此漏洞。开发者需要仔细设计外部调用,并始终检查和更新合约的状态,例如在满足发送资金的请求之前减少以太币余额。添加重入防护锁可以通过锁定合约来防止多个函数同时执行。各种审计工具,如 Slither 和 Securify,可以检查是否存在不同类型重入漏洞。

示例: 在 2024 年对 Penpie 去中心化金融 (DeFi) 协议的重入攻击中,攻击者窃取了价值 2700 万美元的以太币(以太坊的原生加密货币)。

2. 预言机操纵和闪电贷攻击

智能合约使用预言机访问和消费来自区块链外部的数据。这使得它们能够与链外系统(如股票市场)交互。不正确或被操纵的预言机数据可能会错误地触发智能合约的执行;这被称为预言机问题。

许多 DeFi 应用都曾利用此方法被攻击,其中最常见的是闪电贷攻击。闪电贷本质上是无抵押贷款,只要在同一笔交易中偿还贷款,借款金额没有限制。攻击者利用这些贷款来扭曲资产价格以产生利润,同时仍遵守区块链的规则。

修复方法: 使用去中心化预言机,如 Chainlink 或 Tellor——甚至多个预言机——是确保合约收到准确数据的最简单方法。这样的预言机使得攻击者干扰数据变得更困难和昂贵。

示例: Abracadabra 去中心化加密资产借贷平台遭遇了一次闪电贷攻击,攻击者窃取了约 1300 万美元的以太币。

3. 不安全的随机性

密码学算法在生成密钥和为智能合约执行其他操作时依赖随机数源。密码学的一个基本原则是随机数必须是不可预测的,虽然这听起来显而易见,但实现起来往往颇具挑战性。如果随机性来源不够强,并且它们生成的值存在任何可预测性,这可能会为攻击者提供多种机会来规避对智能合约完整性至关重要的密码学保护。

修复方法: 仅使用基于公认标准的密码学进行随机数生成。NIST 有一整个研究项目和一套专注于随机数生成的标准。

示例: $FFIST 加密货币代币遭受了一次攻击,导致约 11 万美元的损失。该攻击被追溯到一个可预测的随机性来源。

4. 业务逻辑错误

业务逻辑错误是智能合约内部或智能合约之间的设计错误的通用术语。该设计错误导致合约的行为与其预期行为不同。攻击者可以利用业务逻辑错误来操纵合约并窃取资金。

修复方法: 开发者应彻底测试所有合约代码,包括所有业务逻辑组合,并验证在每种情况下观察到的行为与预期行为完全匹配。考虑使用手动和/或自动流程和工具来分析合约代码中可能存在的业务逻辑错误。

示例: SIR.trading DeFi 协议在 2025 年 3 月遭遇了一次逻辑缺陷攻击,导致约 35.5 万美元被盗。

5. 强制喂送攻击

强制喂送攻击利用了开发者无法阻止智能合约接收以太币这一事实。这使得向任何合约转移以太币变得很容易——强制喂送——从而改变其持有的以太币余额,进而操纵任何仅依赖预期余额进行内部核算的功能逻辑,例如,如果余额增加到某个水平以上则支付奖励。

修复方法: 无法以这种方式阻止合约余额操纵。切勿使用合约的余额作为函数内的检查或防护,因为实际的以太币余额可能高于合约内部代码预期的余额。

6. 缺乏输入验证

所有软件都需要验证输入;这是几十年来软件开发的核心原则。软件开发者假设所有软件输入都符合预期是危险的。攻击者可以精心构造导致崩溃的输入,可能暂时中断对智能合约的访问,或者导致软件以意外方式运行。更令人担忧的是利用输入来更改数据或改变软件本身,这两者都可能被用来操纵智能合约。

修复方法: 开发者应确保所有输入在使用前都经过仔细验证,确认其完全符合预期。在合约中添加双重检查,以防万一某个意外输入设法通过验证并被处理。更多信息,请参阅 Solidity 文档中关于错误处理的部分。

示例: Onyx DeFi 协议因输入验证漏洞遭到攻击,面临 380 万美元的损失。

7. 拒绝服务

与任何在线服务一样,智能合约容易受到 DoS 攻击。通过使身份验证等服务过载,攻击者可以阻止其他合约执行或产生意外的合约回退,例如,未使用的 Gas 被返还,所有状态回退到交易开始执行前的状态。这可能导致拍卖结果或金融交易中使用的价值被操纵以利于攻击者。

修复方法: 使这些攻击对攻击者来说成本高昂是阻止它们的最佳方式。时间锁谜题和 Gas 费用只是增加攻击者成本的一些方法。确保仅调用受信任的合约也能降低 DoS 攻击造成严重问题的可能性。

示例: 这不是攻击的示例,但在 2025 年 4 月,以太坊改进提案 7907 升级获得批准,旨在帮助防止合约因 Gas 计量而成为 DoS 攻击的受害者。

8. 整数下溢和上溢

当算术运算的结果落在固定大小范围的值之外时,就会发生整数下溢和上溢:对于整数类型 uint8 来说,范围是 0 到 255。高于 255 的值会上溢并重置为零,而低于零的值则会重置为 255。这会导致合约状态变量和逻辑发生意外变化,触发无效操作。

修复方法: 自 2020 年底发布 0.8.0 版本以来,Solidity 编译器不再允许可能导致整数下溢和上溢的代码。检查任何使用早期版本编译的、涉及算术运算函数的合约,或者使用类似 SafeMath 的库来检查下溢和上溢问题。

示例: 2025 年 5 月的 Cetus 去中心化交易所黑客攻击,造成了估计 2.23 亿美元的损失,原因是遗漏了代码溢出检查。

9. 访问控制漏洞

区块链对任何人都是可访问的。切勿将机密或敏感信息保存到区块链,除非它已加密。智能合约内的状态变量和函数也可能对其他智能合约可见和可访问,这使得它们容易被滥用。

修复方法: 开发者应始终遵循最小权限原则实施适当的访问控制,使用 Solidity 的状态变量和函数可见性说明符来分配必要的最低可见性级别,并仅此而已。

示例: KiloEx 去中心化交易所因智能合约中缺乏访问控制而遭受了约 700 万美元的损失。

10. Gas 恶意消耗

要在以太坊区块链平台上执行交易或执行智能合约,用户必须支付 Gas 费。支付 Gas 费是为了激励验证者(矿工)投入验证交易所需的资源。Gas 的价格由交易时的供应、需求和网络容量决定。

当用户发送执行目标智能合约所需的 Gas 量,但不足以执行子调用——它向其他合约发出的调用时,就会发生 Gas 恶意消耗。如果合约不检查执行子调用所需的 Gas 是否可用,则子调用将无法按预期执行。这可能对应用程序的逻辑产生重大影响。

修复方法: 目前没有有效的技术来防止 Gas 恶意消耗。开发者应编码合约,使其设置要发送的 Gas 量,而不是由用户设置。然而,Gas 成本的上升可能意味着交易失败。

11. 交易顺序依赖性攻击(抢先交易)

智能合约从作为待处理交易提交到网络的那一刻起就是公开可见的。这使得区块的矿工可以选择 Gas 费用最高的交易。例如,用户可以包含优先费用(小费)以激励矿工在同一个区块中将他们的交易优先于其他交易处理。然而,这也使得攻击者能够寻找机会,通过提交一份相同但 Gas 费更高的合约,使他们的合约被优先处理,从而抢先执行有利可图的合约。由于这些攻击必须在几分之一秒内实施,它们通常由机器人或矿工自己执行。

修复方法: 这些攻击很难避免。一种选择是只接受 Gas 价格低于预定阈值的交易。或者,使用“提交-揭示”方案,即用户首先提交一个解决方案哈希值而不是明文解决方案,这样潜在的抢先交易者就无法查看,直到为时已晚。各种智能合约审计工具可以检测代码是否引入了抢先交易漏洞。

12. 时间戳依赖性

执行智能合约的节点会生成时间戳值。由于以太坊平台的分布式特性,几乎不可能保证每个节点上的时间都正确同步。然后,节点可以操纵其使用的时间戳值,以针对任何依赖 block.timestamp 变量执行时间关键型操作的合约发起逻辑攻击。

修复方法: 为避免此漏洞,开发者不应使用 block.timestamp 函数作为控制或逻辑检查,或作为随机性来源。

保持智能合约无漏洞

要使智能合约既智能又安全,开发团队必须从一开始就内置安全性,并严格测试其逻辑和代码执行。

合约代码在部署后很难打补丁。第一次就把安全做好是当务之急。始终遵循智能合约安全最佳实践。除非开发团队包括专门的智能合约安全专家,能够通过单元测试每个函数来审计智能合约代码的逻辑缺陷和其他漏洞,否则请使用专门从事智能合约的审计服务来识别任何安全问题。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计