智能合约安全进阶
安全与审计 讨论了常见漏洞的机理、最小复现代码与基础防御模式。本章在此基础上,深入分析生产环境中由跨合约调用、中间态暴露、编译器实现缺陷以及底层存储特性引发的复合安全风险。
前置知识:EVM 调用语义(CALL / DELEGATECALL / 瞬态存储)参见 EVM 与内部状态;代理存储布局参见 存储布局。
基础漏洞模式与进阶复合风险对照
| 漏洞主题 | 基础模式(单合约/标准调用) | 进阶复合风险(跨合约/协议交互/底层机制) |
|---|---|---|
| 重入攻击 | 单函数 ETH 转账回调 | 跨函数/跨合约状态未同步、只读重入(AMM 价格函数暴露)、代币回调钩子、编译器重入锁分配缺陷 |
| 权限控制 | 单一所有者、修饰器缺失 | tx.origin 鉴权伪造、未保护的 initialize 参数特权注入、瞬态存储槽鉴权穿透 |
| 拒绝服务 | 无界循环、转账失败回滚 | EIP-150 63/64 Gas 截留(Gas Griefing)、Return Bomb 内存二次方消耗、强制定向充值破坏状态不变式 |
| 整数与精度 | 算术加减乘整型溢出 | unchecked 优化边界溢出、除法截断与舍入方向差、ERC-4626 初始存款通胀攻击 |
| 授权竞争 | ERC-20 approve 竞态条件 | 无限授权的长期系统性风险、USDT 强制归零限制、EIP-2612 与 Permit2 签名授权机制与边界 |
| 预言机操纵 | DEX 即时现货价与闪电贷操纵 | 只读重入污染瞬时价格、TWAP 时间加权窗口操纵成本与抗操纵性度量、Chainlink 降级检查完整性 |
| 签名安全 | ecrecover 签名延展性与重放 | EIP-712 域分隔符动态缓存缺陷、假 Permit 实现穿透、ERC-1271 智能合约钱包签名边界 |
| 可升级代理 | 存储槽重叠、开放 initializer | EIP-1967 标准槽与 ERC-7201 命名空间存储规范、Nomad 零哈希哨兵值认证穿透、UUPS 裸实现合约初始化风险 |
典型复合风险的机理分类
- 协议间瞬态不一致暴露(只读重入):被调用协议在执行修改操作时具备重入锁,但其暴露的
view查询接口(如 AMM 的get_virtual_price)未加限制。第三方借贷或清算协议依赖该接口进行抵押品定价时,将读取到未完成记账闭环的中间状态。 - 初始化与配置参数的语义歧义:初始化函数将未验证的哨兵值(如
bytes32(0))直接作为可信根写入存储,导致所有未初始化槽位因默认值为 0 而均被断言判定为已授权或已验证。 - 语言编译器底层代码生成缺陷:合约源码层面的互斥锁修饰器在低版本编译器中被映射到了不同的物理存储槽位,导致多入口间实际上未形成互斥保护。
- 份额换算精度损耗与通胀攻击:Solidity 0.8+ 提供了内建溢出保护,但算术逻辑在份额计算(如
totalAssets / totalSupply)中的除法向下取整特性,使得极低份额攻击者可以通过小额初始质押配合强制转账放大每股净值,导致后续存款人资金被截断稀释。
架构审计通用分析维度
在审计跨合约系统时,应从以下维度建立状态机分析模型:
- 控制权转移点识别:明确交易执行链条中向外部地址转移控制权的所有指令(包括
CALL原语、ETH 转账、ERC-777/721/1155 接收钩子、DELEGATECALL)。 - 外部调用窗口期的存储一致性:验证在控制权转移的瞬间,本合约及关联模块的关键状态(余额映射、总发行量、质押比例)是否已完成闭环更新。
- 预言机数据的抗瞬时操纵性:验证核心计算依赖的价格或状态变量是否可在同一笔交易内通过瞬时操作(闪电贷、极值兑换)被篡改。
- 特权函数与生命周期约束:验证初始化函数与升级接口的调用次数限制、访问权限,以及入参是否与映射类型的默认值发生重叠。
- 状态推进路径的容错性:系统关键业务是否依赖单一外部调用成功或无界数据结构遍历,是否存在降级与资产提取兜底通道。