智能合约安全之调用者身份疑云揭秘
字数 1902 2025-08-22 12:22:59

智能合约安全之调用者身份疑云揭秘

文章前言

本文通过分析一个有趣的合约函数safeSendLp,揭示了在智能合约开发中关于调用者身份验证的关键安全问题。该案例展示了如何通过实践调试来解决审计过程中的疑惑,并提供了重要的安全建议。

当前场景分析

问题函数描述

safeSendLp函数具有以下特点:

  • 使用public修饰,任意用户都可以调用
  • 三个参数:
    • _lpToken:LP Token合约地址
    • _user:接收LP代币的地址
    • _amount:要转账的数量

函数逻辑

  1. 首先调用LP Token合约的balanceOf函数检测合约(PDSWAPXXXX)所持资产数量,赋值给lpBal
  2. 检查lpBal是否大于转账数量:
    • 如果大于,调用safeTransfer_user_amount数量的LP
    • 否则,向_user转账lpBal数量

关键安全问题

争议点在于safeSendLp函数中执行IERC20(_lpToken).safeTransfer(_user,_amount)时:

  • 会调用LP Token合约的transfer函数
  • transfer函数会调用_transfer函数
  • _transfer函数中的_msgSender()(即msg.sender)身份不明确

核心问题_transfer中的msg.sendersafeSendLp函数的调用者还是LP Token合约实例?这将直接影响资产是从哪里扣除的。

调试分析

实验设置

在Remix中部署以下智能合约进行测试:

  1. ERC20代币合约:模拟LP Token合约
    • 添加了mint函数便于测试(实际应有限制)
  2. test合约:包含safeSendLp函数

测试步骤

  1. 部署ERC20代币合约

    • 部署者地址: 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
    • 合约地址: 0xd9145CCE52D386f254917e481eB44e9943F39138
  2. 部署test合约

    • 部署者地址: 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2
    • 合约地址: 0xa131AD247055FD2e2aA8b156A11bdEc81b9eAD95
  3. 调用mint函数

    • 向test合约地址(0xa131AD247055FD2e2aA8b156A11bdEc81b9eAD95)铸币100
    • 确保IERC20(_lpToken).balanceOf(address(this))不为0
  4. 攻击测试

    • 攻击者地址: 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c
    • 接收地址: 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db
    • 初始资产:攻击者0,接收地址0,test合约100
    • 调用safeSendLp函数尝试转账

实验结果

  • 攻击者成功将test合约中的ERC20代币转至指定接收地址
  • 证明_transfer函数中的msg.sender是test合约本身,而非函数调用者
  • 漏洞本质:如果合约持有代币资产,任意用户可通过此函数转走合约资产

安全建议

  1. SafeTransfer函数设计

    • 注意不同平台的实现差异
    • 明确转账操作的上下文环境
  2. 调用者身份验证

    • 对不确定的msg.sender必须进行调试验证
    • 明确函数调用链中的上下文切换
  3. 合约执行环境

    • 区分功能函数是本地执行还是跨合约调用
    • 注意参数传递和返回值处理的差异
  4. 权限控制

    • 关键函数应添加适当的权限修饰符
    • 避免使用public修饰敏感操作
  5. 铸币功能

    • 实际应用中应对mint函数增加权限校验
    • 设置合理的铸币上限

技术要点总结

  1. 调用上下文关键点

    • 当A合约调用B合约的函数时,在B合约中msg.sender是A合约的地址,而非原始交易发起者
    • 这种上下文切换是许多安全问题的根源
  2. 资产转移路径

    safeSendLp  safeTransfer  transfer  _transfer
    

    _transfer中,from地址是msg.sender,此时已是调用safeTransfer的合约地址

  3. 漏洞修复方案

    • 方案1:添加权限控制,限制safeSendLp调用者
    • 方案2:修改转账逻辑,明确指定from地址
    • 方案3:使用transferFrom替代transfer,并提前授权

通过本案例,我们深刻理解了在智能合约开发中"实践出真知"的重要性,对不确定的行为必须通过实际测试验证,不能仅凭主观判断。

智能合约安全之调用者身份疑云揭秘 文章前言 本文通过分析一个有趣的合约函数 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 攻击测试 攻击者地址: 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c 接收地址: 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db 初始资产:攻击者0,接收地址0,test合约100 调用 safeSendLp 函数尝试转账 实验结果 攻击者成功将test合约中的ERC20代币转至指定接收地址 证明 _transfer 函数中的 msg.sender 是test合约本身,而非函数调用者 漏洞本质:如果合约持有代币资产,任意用户可通过此函数转走合约资产 安全建议 SafeTransfer函数设计 注意不同平台的实现差异 明确转账操作的上下文环境 调用者身份验证 对不确定的 msg.sender 必须进行调试验证 明确函数调用链中的上下文切换 合约执行环境 区分功能函数是本地执行还是跨合约调用 注意参数传递和返回值处理的差异 权限控制 关键函数应添加适当的权限修饰符 避免使用 public 修饰敏感操作 铸币功能 实际应用中应对 mint 函数增加权限校验 设置合理的铸币上限 技术要点总结 调用上下文关键点 : 当A合约调用B合约的函数时,在B合约中 msg.sender 是A合约的地址,而非原始交易发起者 这种上下文切换是许多安全问题的根源 资产转移路径 : 在 _transfer 中, from 地址是 msg.sender ,此时已是调用safeTransfer的合约地址 漏洞修复方案 : 方案1:添加权限控制,限制 safeSendLp 调用者 方案2:修改转账逻辑,明确指定from地址 方案3:使用 transferFrom 替代 transfer ,并提前授权 通过本案例,我们深刻理解了在智能合约开发中"实践出真知"的重要性,对不确定的行为必须通过实际测试验证,不能仅凭主观判断。