智能合约安全之调用者身份疑云揭秘
字数 1902 2025-08-22 12:22:59
智能合约安全之调用者身份疑云揭秘
文章前言
本文通过分析一个有趣的合约函数safeSendLp,揭示了在智能合约开发中关于调用者身份验证的关键安全问题。该案例展示了如何通过实践调试来解决审计过程中的疑惑,并提供了重要的安全建议。
当前场景分析
问题函数描述
safeSendLp函数具有以下特点:
- 使用
public修饰,任意用户都可以调用 - 三个参数:
_lpToken:LP Token合约地址_user:接收LP代币的地址_amount:要转账的数量
函数逻辑
- 首先调用LP Token合约的
balanceOf函数检测合约(PDSWAPXXXX)所持资产数量,赋值给lpBal - 检查
lpBal是否大于转账数量:- 如果大于,调用
safeTransfer向_user转_amount数量的LP - 否则,向
_user转账lpBal数量
- 如果大于,调用
关键安全问题
争议点在于safeSendLp函数中执行IERC20(_lpToken).safeTransfer(_user,_amount)时:
- 会调用LP Token合约的
transfer函数 transfer函数会调用_transfer函数_transfer函数中的_msgSender()(即msg.sender)身份不明确
核心问题:_transfer中的msg.sender是safeSendLp函数的调用者还是LP Token合约实例?这将直接影响资产是从哪里扣除的。
调试分析
实验设置
在Remix中部署以下智能合约进行测试:
- ERC20代币合约:模拟LP Token合约
- 添加了
mint函数便于测试(实际应有限制)
- 添加了
- test合约:包含
safeSendLp函数
测试步骤
-
部署ERC20代币合约
- 部署者地址:
0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 - 合约地址:
0xd9145CCE52D386f254917e481eB44e9943F39138
- 部署者地址:
-
部署test合约
- 部署者地址:
0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2 - 合约地址:
0xa131AD247055FD2e2aA8b156A11bdEc81b9eAD95
- 部署者地址:
-
调用mint函数
- 向test合约地址(
0xa131AD247055FD2e2aA8b156A11bdEc81b9eAD95)铸币100 - 确保
IERC20(_lpToken).balanceOf(address(this))不为0
- 向test合约地址(
-
攻击测试
- 攻击者地址:
0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c - 接收地址:
0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db - 初始资产:攻击者0,接收地址0,test合约100
- 调用
safeSendLp函数尝试转账
- 攻击者地址:
实验结果
- 攻击者成功将test合约中的ERC20代币转至指定接收地址
- 证明
_transfer函数中的msg.sender是test合约本身,而非函数调用者 - 漏洞本质:如果合约持有代币资产,任意用户可通过此函数转走合约资产
安全建议
-
SafeTransfer函数设计
- 注意不同平台的实现差异
- 明确转账操作的上下文环境
-
调用者身份验证
- 对不确定的
msg.sender必须进行调试验证 - 明确函数调用链中的上下文切换
- 对不确定的
-
合约执行环境
- 区分功能函数是本地执行还是跨合约调用
- 注意参数传递和返回值处理的差异
-
权限控制
- 关键函数应添加适当的权限修饰符
- 避免使用
public修饰敏感操作
-
铸币功能
- 实际应用中应对
mint函数增加权限校验 - 设置合理的铸币上限
- 实际应用中应对
技术要点总结
-
调用上下文关键点:
- 当A合约调用B合约的函数时,在B合约中
msg.sender是A合约的地址,而非原始交易发起者 - 这种上下文切换是许多安全问题的根源
- 当A合约调用B合约的函数时,在B合约中
-
资产转移路径:
safeSendLp → safeTransfer → transfer → _transfer在
_transfer中,from地址是msg.sender,此时已是调用safeTransfer的合约地址 -
漏洞修复方案:
- 方案1:添加权限控制,限制
safeSendLp调用者 - 方案2:修改转账逻辑,明确指定from地址
- 方案3:使用
transferFrom替代transfer,并提前授权
- 方案1:添加权限控制,限制
通过本案例,我们深刻理解了在智能合约开发中"实践出真知"的重要性,对不确定的行为必须通过实际测试验证,不能仅凭主观判断。