安全与测试
fuzz、威胁向量、随机数攻击、Echidna。
35 分钟
安全与测试
智能合约部署后难以修补,安全与测试必须前置。本课覆盖常见威胁向量、模糊测试(Fuzz)、随机数陷阱与 Echidna 等工具。见 /roadmaps/blockchain。
学习目标
- 识别重入、权限绕过、整数精度等高频漏洞
- 用 Hardhat/Foundry 编写单元测试与 Fuzz 测试
- 了解 Echidna 属性测试的基本思路
威胁向量
| 攻击 | 机制 | 防御 |
|---|---|---|
| 重入 | 外部调用回调再次进入 | Checks-Effects-Interactions;ReentrancyGuard |
| 权限提升 | 缺少 onlyOwner | OpenZeppelin AccessControl |
| 前端运行 | 用户交易被抢跑 | commit-reveal;私有 mempool |
| 预言机操纵 | 瞬时价格失真 | 多源喂价、TWAP |
| 错误随机数 | 用 block.timestamp 当随机 | Chainlink VRF |
重入示例与修复
// 脆弱:先转账后改余额
function withdraw(uint256 amount) external {
require(balances[msg.sender] >= amount);
(bool ok,) = msg.sender.call{value: amount}("");
require(ok);
balances[msg.sender] -= amount; // 太晚了
}
// 修复:先改状态
function withdrawSafe(uint256 amount) external nonReentrant {
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount;
(bool ok,) = msg.sender.call{value: amount}("");
require(ok);
}
Fuzz 与属性测试
Foundry 内置 fuzz:随机 amount 不应破坏总供应量不变量。
function testFuzz_transfer(uint256 amount) public {
amount = bound(amount, 0, token.balanceOf(address(this)));
token.transfer(recipient, amount);
assertGe(token.balanceOf(recipient), 0);
}
Echidna 用 echidna_* 函数声明应始终成立的全局属性,例如「任意地址余额 ≤ totalSupply」,适合发现边界组合漏洞。
练习
- 故意写一个带重入漏洞的 withdraw,用测试证明可被攻击
- 为 ERC-20 添加一条 Foundry fuzz 测试
- 运行 Slither,修复至少一条 medium 以上告警
小结
链上无 hotfix,测试与审计就是发布流程的一部分。 下一课讲解部署、RPC 节点与运维监控。
加载本节练习…