Caloogy 文章
English

无限额授权:你可能早就把钱"借"出去了

日期
2025 年 9 月 10 日
系列
链上
阅读
3 分钟
主题
Erc 20ApproveAllowanceRevoke

一句话版本

在以太坊上,让一个 DeFi 应用"使用"你的代币,需要先签一笔 approve,授权它从你的地址转走最多 X 个代币。为了省事,绝大多数应用默认请求无限额授权,而且这份授权永不过期。 只要那个合约日后出了漏洞、被升级成恶意版本、或者你当初授权的根本就是钓鱼合约,你钱包里的这种代币就可以被随时转走,不需要你再签任何东西。所以要做两件事:授权时只给精确额度,以及定期检查并撤销不用的授权。

全文分三层:

  1. 通俗理解:签了一张不限额、不过期的委托书
  2. 实际用处:真实事件,以及怎样检查和撤销
  3. 原理与工程要点:approve / transferFrom 的机制、calldata 解码、精确额度

第一层:通俗理解

1.1 一张不限额、不过期的委托书

你想用一个交易所兑换 100 USDC。流程是两步:

  1. 授权:告诉 USDC 合约"允许这个交易所合约从我这里转走 USDC";
  2. 兑换:交易所合约把你的 100 USDC 转走,换给你别的代币。

问题在第 1 步。为了让你下次兑换时不用再授权一次,应用通常请求的额度是无限。这相当于签了一张委托书:"此人可以随时从我的账户里转走任意数量的 USDC,永久有效。"

1.2 什么时候出事

情况 结果
被授权的合约有漏洞 攻击者利用漏洞,以合约身份把所有授权过的用户的钱转走
合约是可升级的,管理员私钥被盗 升级成恶意版本,同上
你当初授权的就是钓鱼合约 攻击者想什么时候转就什么时候转

可怕之处在于:授权可能是几年前签的,你早就忘了。


第二层:实际用处

怎么检查和撤销:

Caloogy Wallet 也提供授权检查和一键撤销。


第三层:原理与工程要点

3.1 ERC-20 的授权机制

ERC-20 代币合约内部维护一张表 allowance[owner][spender]:

function approve(address spender, uint256 amount) returns (bool);   // owner 调用:设置额度
function transferFrom(address from, address to, uint256 amount);     // spender 调用:在额度内转走 from 的代币

3.2 从 calldata 认出一笔授权

钱包发出的交易里,data 字段的前 4 个字节是函数选择器,等于函数签名 Keccak-256 哈希的前 4 字节:

函数 选择器
approve(address,uint256) 0x095ea7b3
transfer(address,uint256) 0xa9059cbb

一笔无限额授权的 calldata 长这样:

0x095ea7b3
  000000000000000000000000 <spender 地址 20 字节>
  ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

看到 095ea7b3 后面跟着一整串 f,就是无限额授权。(选择器已用 Python 验算。)

3.3 工程上的防护

措施 说明
默认精确额度 应用请求无限额时,钱包把额度改成本次实际需要的数量(MetaMask 已经支持用户手动修改)
识别所有授权类调用 approve、increaseAllowance、setApprovalForAll,以及 Permit / Permit2 签名(见《盲签与所见即所签》)
授权对象检查 spender 是不是已知合约?是不是普通地址(普通地址作为 spender 几乎一定是钓鱼)?
定期扫描 列出地址上所有仍然有效的授权,标出"无限额""对象未知""很久没用"
撤销是低风险操作 撤销只会让账户更安全,风控上可以给最低风险等级,减少用户撤销的阻力
上链核对 授权列表若来自第三方索引服务,展示前用 allowance(owner, spender) 直接在链上读一次当前额度

3.4 新的方向


总结

层次 要点
直觉 无限额授权 = 不限额、不过期的委托书
风险 被授权合约出问题,你什么都不做也会被转走
识别 0x095ea7b3 + 一串 f;还有 setApprovalForAll 和 Permit 签名
防护 默认精确额度;定期检查;用完就撤销