智能合约审计系列————2、权限隐患&条件竞争
字数 2388 2025-08-22 12:22:30
智能合约审计系列:权限隐患与条件竞争漏洞详解
0x00 前言
本文是智能合约审计系列的第二篇,重点探讨智能合约开发中常见的权限隐患和条件竞争问题。这些安全问题往往由于开发者疏忽或设计不当导致,可能造成严重的安全后果。
0x01 基础知识
权限的概念
- 权限定义:指对某事项进行决策的范围和程度
- 智能合约用户分类:
- 合约owner:拥有合约全部权限,可执行所有函数
- 普通用户:只能执行约定范围内的函数,无法执行权限校验函数(如onlyOwner修饰的函数)
Solidity函数可见性
| 修饰符 | 可调用范围 |
|---|---|
| public | 合约内部函数、继承合约、外部合约均可调用(默认可见性) |
| private | 仅限合约内部函数调用(继承合约调用会编译报错) |
| internal | 合约内部函数及继承合约可调用 |
| external | 仅限外部合约调用 |
0x02 权限隐患
构造函数权限问题
构造函数的作用
用于初始化合约对象的状态变量,具有以下特点:
- 函数名与合约名相同(0.4.22版本前)
- 可用public或internal修饰
- 若有payable修饰,则必须是public类型
- 若带参数,必须放在合约第一个函数位置
版本变化
- 0.4.22版本前:构造函数名必须与合约名相同
- 0.4.22版本后:引入
constructor关键字替代旧语法
常见安全问题
-
构造函数名与合约名不一致
- 旧版本中,构造函数名若与合约名不一致(如大小写错误、拼写错误),该函数会变为普通public函数
- 示例:ReaperCoin11合约中
reaper11函数本应是构造函数,但因名称不匹配成为公开函数
-
constructor使用不规范
- 新版本中错误使用
function constructor()或function Constructor()会使构造函数变为普通函数 - 示例:MDOT合约和TOGToken合约中的不规范写法
- 新版本中错误使用
安全建议:使用规范的构造函数定义方法,新版本统一使用constructor关键字
普通函数权限问题
常见漏洞模式
-
特权函数滥用
- 示例1:Token合约中的
burn函数- 使用
onlyAuthorized修饰器(实际为onlyOwner) - owner可销毁任意用户的代币(包括归零)
- 使用
- 示例2:sacToken合约中的
melt函数- 使用
onlyCFO修饰器(实际为onlyOwner) - owner可销毁任意用户的代币
- 使用
- 示例1:Token合约中的
-
后门函数
- 开发者预留的未公开特权函数
- 通常使用隐蔽的修饰器或权限检查
安全建议:
- 严格把控函数逻辑流程
- 合理设置参数定义
- 谨慎使用修饰器和可见性修饰词
- 避免预留不必要的特权函数
0x03 条件竞争
基本概念
条件竞争(竞态条件)指由于并发操作处理不当导致的安全问题。在以太坊中表现为:
- 区块链交易公开可见
- 恶意用户可观察未决交易并抢先执行
- 典型场景:ERC20的approve-transferFrom流程
ERC20中的条件竞争问题
标准approve函数问题
function approve(address _spender, uint256 _value) public returns (bool)
攻击场景:
- 用户A批准用户B转账额度N
- 一段时间后,用户A欲将额度改为M,再次调用approve
- 用户B在第二次approve被处理前,迅速用transferFrom转走N
- 第二次approve生效后,用户B又获得M额度
- 最终用户B拥有N+M的转账额度
解决方案讨论
-
Ethereum官方建议:
- 修改额度时必须先设为0再设新值
- 代码实现:
require((_value == 0) || (allowed[msg.sender][_spender] == 0)); - 问题:违反ERC20标准规范
-
OpenZeppelin方案:
- 使用
increaseApproval和decreaseApproval替代直接approve - 但仍保留标准approve函数(无require检查)
- 问题:若用户仍调用approve而非新增函数,问题依旧存在
- 使用
-
实际合约中的三种处理方式:
- 类型1:在approve中增加require检查(安全但非标准)
- 类型2:使用increase/decreaseApproval,保持标准approve(仍不安全)
- 类型3:结合前两种,既用新函数又在approve中加检查(最安全但最不标准)
安全与标准的权衡:
- 安全角度:类型1和类型3更安全
- 标准角度:类型2符合ERC20标准
0x04 总结与最佳实践
开发建议
-
建立严格的开发规范
- 函数命名、权限控制、可见性使用等
- 特别是构造函数的使用规范
-
持续学习安全知识
- 跟踪公开的合约漏洞案例
- 了解攻击手法和防御方案
-
权限控制原则
- 最小权限原则
- 避免不必要的特权函数
- 关键操作设置多重验证
-
条件竞争防御
- 根据项目需求在安全与标准间权衡
- 考虑使用增量式授权而非直接覆盖
- 重要操作添加状态检查
-
专业审计
- 上线前进行专业安全审计
- 包括静态分析和动态测试
经典格言
"道路千万条,安全第一条。编码不规范,亲人两行泪"
通过规范开发流程、严格权限控制和正确处理并发问题,可显著提高智能合约的安全性,避免重大损失。