本文介绍了 Solodit 社区维护的智能合约安全审计检查表,强调了智能合约安全的重要性,并列举了由于漏洞导致重大经济损失的案例。文章详细介绍了使用该检查表进行智能合约审计的前提条件、所需资源,并深入探讨了包括重入攻击、拒绝服务攻击、抢跑交易等常见的漏洞及其缓解措施,同时还介绍了安全开发的最佳实践。文章还提到了2025年最新的安全工具和技术更新。
智能合约是像以太坊这样的区块链平台上,去中心化应用(dApp)的支柱,它们已经改变了去中心化金融(DeFi)、非同质化代币(NFTs)和去中心化自治组织(DAOs)等行业。 通过自动化无需信任的交易,它们消除了中间人,确保了透明度和效率。 然而,它们的不可变性和资金风险使它们成为攻击者的主要目标。 正如 2016 年 The DAO 被攻击事件所见,一个漏洞可能导致灾难性的损失,该事件耗尽了 360 万个 ETH(当时约为 7000 万美元),或者如 2021 年 Poly Network 被利用事件,由于有缺陷的跨链逻辑,导致超过 6 亿美元被盗。
Solodit 清单是由 Solodit 社区策划的,是一种经过实战检验的智能合约审计框架。 它对漏洞进行分类,提供来自专业审计的真实示例,并提供可操作的检查以确保强大的安全性。 本文是 智能合约安全:Solodit 清单系列 的前言,介绍了该清单,探讨了关键漏洞,并为开发人员和审计人员提供了一个保护截至 2025 年 6 月的基于以太坊的智能合约的路线图。
智能合约在安全至关重要的高风险环境中运行。 以下是审计至关重要的关键原因:
截至 2025 年 6 月,DeFi 协议管理的资金额巨大,以太坊和 EVM 兼容链上的总锁定价值 (TVL) 超过 1200 亿美元。 这使得智能合约成为攻击者有利可图的目标。 例如:
以太坊上的智能合约默认是不可变的,这意味着除非显式实现可升级机制(例如,像 UUPS 这样的代理模式),否则漏洞无法在部署后进行修补。 即使那样,升级也会引入它们自己的风险,例如存储冲突或未经授权的升级。 彻底的审计确保合约从第一天起就是安全的。
智能合约与外部系统交互,包括:
Solodit 清单通过提供一种系统的方法来识别诸如重入、拒绝服务 (DoS)、抢先交易和访问控制缺陷之类的漏洞,从而应对这些挑战。 它被像 Trail of Bits 和 OpenZeppelin 这样的顶级审计公司使用,以确保全面的安全检查。
审计智能合约需要技术专业知识、安全意识和正确的工具。 以下是如何开始:
静态分析工具:
动态分析工具:
开发环境:
文档:
本系列旨在使读者能够:
Solodit 清单根据真实世界的审计对漏洞进行分类,提供具体的检查和缓解策略。 下面,我们通过代码示例探讨关键漏洞、其影响以及如何解决它们。 未来的文章将更深入地探讨每个漏洞。
描述:当合约调用一个外部合约时,如果该外部合约在状态更新之前递归地回调到原始合约,就会发生重入,从而允许攻击者操纵变量。 2016 年的 The DAO 被黑事件利用了这一点,耗尽了数百万美元。
有漏洞的代码:
function withdraw(uint256 amount) public {
require(balances[msg.sender] >= amount, "Insufficient balance");
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
balances[msg.sender] -= amount;
}
攻击者可以在 balances[msg.sender] 更新之前重新进入 withdraw,从而反复耗尽资金。
Solodit 检查:
缓解措施:
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract SecureFund is ReentrancyGuard {
mapping(address => uint256) public balances;
function withdraw(uint256 amount) public nonReentrant {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
}
描述:DoS 攻击会中断合约功能,通常是通过消耗过多的 gas 或阻止执行。 例如,迭代一个无界数组可能会超过区块 gas 限制。
有漏洞的代码:
function distributeRewards(address[] memory recipients) public {
for (uint256 i = 0; i < recipients.length; i++) {
recipients[i].transfer(1 ether);
}
}
大型 recipients 数组可能会导致 gas 限制错误,从而停止执行。
Solodit 检查:
缓解措施:
mapping(address => uint256) public rewards;
function claimReward() public {
uint256 reward = rewards[msg.sender];
require(reward > 0, "No reward available");
rewards[msg.sender] = 0;
(bool success, ) = msg.sender.call{value: reward}("");
require(success, "Transfer failed");
}
描述:攻击者监控内存池中待处理的交易,并提交更高 gas 的交易以首先执行,从而从订单优先级中获利。 在 DEX 和拍卖中很常见。
例子:用户在 Uniswap 上提交代币购买请求。 攻击者抢先交易,首先购买代币并以更高的价格出售。
Solodit 检查:
缓解措施:
mapping(address => bytes32) public commitments;
function commitOrder(bytes32 commitment) public {
commitments[msg.sender] = commitment;
}
function revealOrder(uint256 amount, bytes32 secret) public {
require(keccak256(abi.encodePacked(amount, secret)) == commitments[msg.sender], "Invalid commitment");
// 处理订单
}
描述:在 Solidity 0.8.0 之前,算术运算可能会环绕,从而导致意外行为(例如,uint256(0) - 1 产生 2²⁵⁶ - 1)。
Solodit 检查:
缓解措施:
// Solidity >= 0.8.0
function safeSubtract(uint256 a, uint256 b) public pure returns (uint256) {
return a - b; // 在下溢时恢复
}
描述:依赖外部预言机(例如,用于价格馈送)的合约容易受到操纵数据的影响,如 2021 年 Cream Finance 被黑事件(1.3 亿美元)所见。
Solodit 检查:
缓解措施:
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract PriceFeed {
AggregatorV3Interface priceFeed;
constructor(address _priceFeed) {
priceFeed = AggregatorV3Interface(_priceFeed);
}
function getPrice() public view returns (uint256) {
(, int256 price,,,) = priceFeed.latestRoundData();
require(price > 0, "Invalid price");
return uint256(price);
}
}
描述:不正确的访问控制允许未经授权的用户执行受限制的函数,如 2022 年 Nomad Bridge 被黑事件(1.9 亿美元)所见。
有漏洞的代码:
function updateConfig(address newConfig) public {
config = newConfig; // 没有访问控制
}
Solodit 检查:
缓解措施:
import "@openzeppelin/contracts/access/AccessControl.sol";
contract ConfigManager is AccessControl {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
address public config;
constructor() {
_grantRole(ADMIN_ROLE, msg.sender);
}
function updateConfig(address newConfig) public onlyRole(ADMIN_ROLE) {
config = newConfig;
}
}
描述:如果状态变量在版本之间未对齐,则使用代理模式(例如,UUPS)的可升级合约存在存储冲突的风险。
Solodit 检查:
缓解措施:使用 OpenZeppelin 的 UUPSUpgradeable 并使用以下命令验证布局:
slither-check-upgradeability --proxy Proxy.sol --implementation NewImplementation.sol
为了补充 Solodit 清单,请采用以下实践:
截至 2025 年 6 月,智能合约安全格局已发生变化:
保护智能合约是 Web3 生态系统中的一项关键任务。 Solodit 清单提供了一个结构化的、社区驱动的框架,用于识别和缓解漏洞,从重入到存储冲突。 通过将其与最佳实践、像 Slither 和 Foundry 这样的高级工具以及安全至上的意识相结合,开发人员和审计人员可以构建和部署强大的合约。
本文开启了 智能合约安全:Solodit 清单系列。 未来的文章将提供对每个漏洞的深入分析,包括:
无论你是 Solidity 开发人员、DeFi 创始人还是安全研究人员,掌握 Solodit 清单对于在去中心化世界中构建无需信任的安全系统至关重要。
智能合约是像以太坊这样的区块链平台上,去中心化应用(dApp)的支柱,它们已经彻底改变了去中心化金融(DeFi)、非同质化代币(NFTs)和去中心化自治组织(DAOs)等行业。 通过自动化无需信任的交易,智能合约消除了中间人,增强了透明度,并提高了效率。
然而,它们的不可变性和巨额资金风险使它们成为攻击者的首选目标。 单个漏洞可能会导致灾难性的损失。 考虑:
为了应对这些挑战,由 Solodit 社区策划的 Solodit 清单 提供了一个强大的智能合约审计框架。 它对常见漏洞进行分类,提供来自专业审计的真实案例,并概述了可操作的安全检查。 本文介绍了该清单,并为智能合约安全:Solodit 清单系列奠定了基础。
DeFi 协议总共管理着超过 1200 亿美元的 TVL(截至 2025 年 6 月),这使它们成为利润丰厚的目标。 值得注意的事件:
智能合约一旦部署就不可变。 除非显式设计了可升级性(例如,UUPS,透明代理),否则无法修复缺陷。 即使是可升级的合约也会带来未经授权的升级或存储未对齐等风险。
智能合约与以下项目交互:
这些交互创建了复杂的攻击面,例如竞争条件、抢先交易或预言机操纵。 例如,2021 年的 Cream Finance 被黑事件(1.3 亿美元)利用了被操纵的预言机价格馈送,突出了对强大的外部数据验证的需求。
Solodit 清单通过提供一种系统的方法来识别诸如重入、拒绝服务 (DoS)、抢先交易和访问控制缺陷之类的漏洞,从而应对这些挑战。 它被像 Trail of Bits 和 OpenZeppelin 这样的顶级审计公司使用,以确保全面的安全检查。
描述: 当合约调用一个外部合约时,如果该外部合约在状态更新之前递归地回调到原始合约,就会发生重入,从而允许攻击者操纵变量。 2016 年的 The DAO 被黑事件利用了这一点,耗尽了数百万美元。
有漏洞的代码:
function withdraw(uint256 amount) public {
require(balances[msg.sender] >= amount, "Insufficient balance");
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
balances[msg.sender] -= amount;
}
攻击者可以在 balances[msg.sender] 更新之前重新进入 withdraw,从而反复耗尽资金。
Solodit 检查:
缓解措施:
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract SecureFund is ReentrancyGuard {
mapping(address => uint256) public balances;
function withdraw(uint256 amount) public nonReentrant {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
}
}使用 OpenZeppelin 的 ReentrancyGuard 并遵循检查-效果-交互模式。
描述: DoS 攻击会中断合约功能,通常是通过消耗过多的 gas 或阻止执行。 例如,迭代一个无界数组可能会超过区块 gas 限制。
有漏洞的代码:
function distributeRewards(address[] memory recipients) public {
for (uint256 i = 0; i < recipients.length; i++) {
recipients[i].transfer(1 ether);
}
}
大型 recipients 数组可能会导致 gas 限制错误,从而停止执行。
Solodit 检查:
缓解措施(拉取模式):
mapping(address => uint256) public rewards;
function claimReward() public {
uint256 reward = rewards[msg.sender];
require(reward > 0, "No reward available");
rewards[msg.sender] = 0;
(bool success, ) = msg.sender.call{value: reward}("");
require(success, "Transfer failed");
}避免在同一流程中进行循环和外部调用。
描述: 攻击者监控内存池中待处理的交易,并提交更高 gas 的交易以首先执行,从而从订单优先级中获利。 在 DEX 和拍卖中很常见。 缓解措施(提交-揭示):
例子: 用户在 Uniswap 上提交代币购买请求。 攻击者抢先交易,首先购买代币并以更高的价格出售。
Solodit 检查:
缓解措施:
mapping(address => bytes32) public commitments;
function commitOrder(bytes32 commitment) public {
commitments[msg.sender] = commitment;
}
function revealOrder(uint256 amount, bytes32 secret) public {
require(keccak256(abi.encodePacked(amount, secret)) == commitments[msg.sender], "Invalid commitment");
// 处理订单
}
描述: 在 Solidity 0.8.0 之前,算术运算可能会环绕,从而导致意外行为(例如,uint256(0) - 1 产生 ²²⁵⁶ - 1)。
Solodit 检查:
缓解措施:
// Solidity >= 0.8.0
function safeSubtract(uint256 a, uint256 b) public pure returns (uint256) {
return a - b; // 在下溢时恢复
}
描述: 依赖外部预言机(例如,用于价格馈送)的合约容易受到操纵数据的影响,如 2021 年 Cream Finance 被黑事件(1.3 亿美元)所见。
Solodit 检查:
缓解措施:
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract PriceFeed {
AggregatorV3Interface priceFeed;
constructor(address _priceFeed) {
priceFeed = AggregatorV3Interface(_priceFeed);
}
function getPrice() public view returns (uint256) {
(, int256 price,,,) = priceFeed.latestRoundData();
require(price > 0, "Invalid price");
return uint256(price);
}
}
描述: 不正确的访问控制允许未经授权的用户执行受限制的函数,如 2022 年 Nomad Bridge 被黑事件(1.9 亿美元)所见。
有漏洞的代码:
function updateConfig(address newConfig) public {
config = newConfig;
}
Solodit 检查:
缓解措施:
import "@openzeppelin/contracts/access/AccessControl.sol";
contract ConfigManager is AccessControl {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
address public config;
constructor() {
_grantRole(ADMIN_ROLE, msg.sender);
}
function updateConfig(address newConfig) public onlyRole(ADMIN_ROLE) {
config = newConfig;
}
}
描述: 如果状态变量在版本之间未对齐,则使用代理模式(例如,UUPS)的可升级合约存在存储冲突的风险。
Solodit 检查:
缓解措施: 使用 OpenZeppelin 的 UUPSUpgradeable 并使用以下命令验证布局:
slither-check-upgradeability --proxy Proxy.sol --implementation Impl.sol
为了补充 Solodit 清单,请采用以下实践:
保护智能合约是 Web3 生态系统中的一项关键任务。 Solodit 清单提供了一个结构化的、由社区驱动的框架,用于识别和缓解漏洞,从重入到存储冲突。 通过将其与最佳实践、像 Slither 和 Foundry 这样的高级工具以及安全至上的意识相结合,开发人员和审计人员可以构建和部署强大的合约。
本文开启了 智能合约安全:Solodit 清单系列。 未来的文章将提供对每个漏洞的深入分析,包括:
无论你是 Solidity 开发人员、DeFi 创始人还是安全研究人员,掌握 Solodit 清单对于在去中心化世界中构建无需信任的安全系统至关重要。✏️ 系列路线图:
请继续关注,以掌握每种漏洞类别,模拟攻击并实施万无一失的防御。
- 原文链接: medium.com/@ankitacode11...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!