3、智能合约安全威胁模型:常见攻击面分类与威胁建模方法

智能合约的安全审计不能停留在“找漏洞”的被动层面,而应上升为“系统化识别风险”的主动工程。威胁建模(Threat Modeling)正是将安全从代码细节提升到架构与业务逻辑层面的核心方法。本章将围绕常见攻击面分类、威胁建模流程,以及STRIDE与攻击树两种经典方法在合约审计中的落地应用展开,帮助审计人员建立“先建模、后审计”的思维框架。

智能合约威胁建模全景图 威胁建模核心流程 攻击面分类(5大类) 重入攻击 整数溢出/精度损失 访问控制缺陷 拒绝服务(DoS) 逻辑/业务逻辑漏洞 STRIDE 威胁分类 S - 身份欺骗 T - 数据篡改 R - 抵赖 I - 信息泄露 D - 拒绝服务 E - 权限提升 攻击树:根节点 → 子目标 → 叶节点 攻击面识别 → STRIDE分类 → 攻击树细化 → 审计验证

3.1 常见攻击面分类:从漏洞模式到审计关注点

智能合约的攻击面与传统Web应用有显著差异。由于区块链的不可篡改性和执行环境的确定性,攻击者往往利用合约的外部调用机制、数值计算边界、权限管理缺陷以及资源消耗特性发起攻击。审计人员必须对以下五类攻击面建立系统认知。

3.1.1 重入攻击(Reentrancy)

核心原理:当合约A调用外部合约B时,控制权暂时转移给B。若B在回调中再次调用A的未完成函数,就会形成重入。经典的DAO攻击(2016年)导致360万ETH被盗,正是利用withdraw()函数在更新余额之前发起外部调用。

判断方法:审计时需检查所有外部调用(call、delegatecall、transfer、send)是否遵循“先更新状态,再交互”(Checks-Effects-Interactions)模式。同时关注跨函数重入(Cross-function Reentrancy)和只读重入(Read-only Reentrancy)等变体。

实战要点:使用OpenZeppelin的ReentrancyGuard修饰器是最直接的防护,但审计时仍需验证其是否覆盖所有可能的重入路径,尤其是_beforeTokenTransfer等钩子函数中的隐式调用。

3.1.2 整数溢出与精度损失

Solidity 0.8.0之前默认不检查溢出,导致uint256加减乘除可能回绕。即使使用SafeMath,精度截断问题依然存在——例如在代币兑换中,amount * rate / 10000的整数除法会丢失余数,攻击者可利用多次小额操作累积误差。

漏洞类型典型场景审计关注点
算术溢出余额累加、奖励计算是否使用SafeMath或0.8+内置检查
精度截断汇率换算、份额分配除法顺序、舍入方向是否有利于攻击者
下溢用户余额扣减是否先校验余额再执行减法

3.1.3 访问控制缺陷

访问控制漏洞往往源于权限校验缺失或角色管理混乱。常见模式包括:onlyOwner修饰器未正确继承、构造函数拼写错误导致无法初始化、tx.origin误用(钓鱼攻击)、以及delegatecall导致的权限提升。

风险提示:审计时需绘制完整的“函数-角色”权限矩阵,特别关注初始化函数(initialize)是否可被任意用户抢先调用(Front-running),以及selfdestruct后合约地址被重新创建时的权限重置问题。

3.1.4 拒绝服务(DoS)

智能合约的DoS攻击不依赖网络流量,而是通过逻辑阻塞或资源耗尽实现。典型场景包括:require条件被永久破坏、循环遍历动态数组导致Gas超限、外部调用失败导致整个交易回滚(如transfer失败阻断退款流程)。

3.1.5 业务逻辑漏洞

这类漏洞最隐蔽,往往需要结合业务场景理解。例如:治理合约中的投票权重计算错误、借贷协议中的清算阈值偏差、NFT盲盒的随机数可预测性等。审计时需将合约代码映射到业务流程图,识别状态机转换中的非法路径。

3.2 威胁建模方法:从代码审计到系统化风险识别

威胁建模的核心价值在于提前发现设计层面的风险,而非仅依赖代码扫描。在智能合约审计中,建议采用以下四步流程:

  1. 系统分解:明确合约的边界、外部依赖(预言机、其他合约)、数据流和信任边界。
  2. 威胁识别:基于攻击面分类,枚举所有可能的攻击路径。
  3. 风险排序:根据攻击难度、影响范围、资产损失进行优先级排序。
  4. 缓解验证:检查现有防护措施是否覆盖已识别的威胁。

3.2.1 STRIDE在合约审计中的适配

STRIDE(Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege)源自微软,用于系统化威胁分类。在智能合约中,各字母的映射如下:

  • S(身份欺骗):攻击者伪装成其他地址调用函数,如未校验msg.sender的权限。
  • T(数据篡改):修改合约状态变量,如操纵价格预言机。
  • R(抵赖):用户否认发起过某笔交易,通常依赖链上签名验证。
  • I(信息泄露):私有数据被读取,如private变量实际可被链上读取。
  • D(拒绝服务):合约功能被永久或暂时阻塞。
  • E(权限提升):从普通用户升级为管理员,如利用delegatecall漏洞。
实践建议:STRIDE更适合作为“检查清单”而非“分析引擎”。审计时可将每个STRIDE类别与攻击面分类交叉,形成二维矩阵,确保覆盖所有风险维度。

3.3 攻击树在合约审计中的应用

攻击树(Attack Tree)以根节点表示攻击者的最终目标,子节点表示达成目标所需的中间步骤,叶节点表示具体攻击动作。这种结构特别适合分析复杂的多步攻击链。

3.3.1 构建攻击树的步骤

  1. 定义根目标:例如“盗取合约中所有ETH”。
  2. 分解子目标:需要“获得owner权限”或“绕过提款校验”。
  3. 细化叶节点:如“利用delegatecall修改owner”、“构造恶意calldata触发重入”。
  4. 标注条件:每个节点标注AND/OR关系,以及所需前置条件。

3.3.2 案例拆解:基于攻击树的借贷协议审计

背景与约束:某DeFi借贷协议允许用户抵押ETH借出DAI,清算阈值为150%。审计目标为“攻击者无需抵押即可提取DAI”。

攻击树构建:

根目标:无抵押提取DAI
├── OR 1: 操纵价格预言机
│   ├── AND 1.1: 控制预言机合约
│   │   ├── 1.1.1 利用owner未初始化漏洞
│   │   └── 1.1.2 闪电贷操纵Uniswap价格
│   └── AND 1.2: 利用价格更新延迟
│       └── 1.2.1 在价格更新前提交清算
├── OR 2: 绕过抵押校验
│   ├── 2.1 利用重入漏洞在抵押前提取
│   └── 2.2 利用精度截断使抵押价值计算为0
└── OR 3: 直接修改余额映射
    └── 3.1 利用storage碰撞漏洞

分析判断过程:审计团队通过攻击树发现,路径1.1.2(闪电贷操纵预言机)的可行性最高,因为该协议使用Uniswap TWAP但时间窗口过短(仅5分钟)。进一步检查发现,预言机更新函数updatePrice()未设置访问控制,任何用户均可触发。

实际操作步骤:审计团队编写PoC,通过闪电贷借入大量USDC,在Uniswap V3中拉高ETH/USD价格,随后调用updatePrice()提交恶意价格,再以极低抵押率借出DAI。PoC在测试网复现成功。

结果对比:修复前,攻击者可无抵押提取全部DAI;修复后(增加价格更新权限控制、延长TWAP窗口至30分钟),攻击路径被有效阻断。

复盘经验:攻击树的价值在于将“抽象目标”转化为“可验证的测试用例”。审计报告中应附上攻击树图,帮助开发团队理解漏洞间的关联性。

3.4 常见误区与总结复盘

在威胁建模实践中,审计人员常陷入以下误区:

  • 误区一:将威胁建模等同于漏洞扫描,忽略设计层面的风险。
  • 误区二:STRIDE分类生搬硬套,未结合智能合约的独特执行模型。
  • 误区三:攻击树只画不验证,未将叶节点转化为实际测试用例。
  • 误区四:过度关注已知漏洞模式,忽视业务逻辑的“合理但危险”路径。
总结:威胁建模不是一次性的前置活动,而应贯穿审计全程。建议在审计初期建立攻击面清单,中期使用STRIDE进行系统性排查,后期用攻击树验证复杂攻击链。最终交付物应包含威胁模型文档、风险矩阵和可执行的测试用例,而非仅是一份漏洞列表。