2026-07-08 | 针对 x402 类稳定币支付的最大盲区
一、什么是盲签?
盲签 = 在"看不懂/没解码"签名内容的情况下就签名授权。
x402 付款时,客户端要签一笔 EIP-3009 transferWithAuthorization 消息。这条消息本质是一串结构化数据(EIP-712),字段包括:
| 字段 | 含义 | 攻击者可做的手脚 |
|---|---|---|
to |
收款地址 | 换成攻击者地址 |
value |
转账金额 | 402 显示 1 USDC,实际让你签 10000 |
validAfter/validBefore |
有效期 | 拉长有效期便于后续利用 |
nonce |
防重放随机数 | · |
chainId / domain |
链与合约域 | 换成别的链/合约 |
盲签的危险:钱包/Agent 只显示"请签名"或一串十六进制,用户/Agent 看不到真实的 to 和 value 就点了同意。
攻击者于是可以:
- 金额调包:界面说 1 USDC,签的却是天价;
- 地址调包:钱打给攻击者而非商家;
- 消息类型调包:更狠的是根本不是 transferWithAuthorization,而是 approve/permit 这类授权额度消息 · 一旦签下,攻击者可反复把你钱包掏空,不止一笔。
一句话:盲签 = 把一张空白支票签了名递出去。 破解之道就是"签之前先把支票内容打印出来看清楚" · 即解码 + 白名单校验。
二、攻击 vs 防御 对照时序图
sequenceDiagram
autonumber
participant Agent as AI Agent / 钱包
participant Guard as 安全校验层<br/>(解码+白名单)
participant Evil as 恶意服务器
participant Chain as 链上 (USDC 合约)
Agent->>Evil: GET /resource
Evil-->>Agent: 402 显示"仅需 1 USDC"<br/>但内嵌授权是: to=攻击者, value=10000
alt ❌ 盲签路径(危险)
Agent->>Agent: 不解码, 直接签"请求签名"
Agent->>Chain: 广播 EIP-3009 授权
Chain-->>Evil: 10000 USDC 转给攻击者
Note over Agent,Chain: 不可逆, 无退款, 损失已成
else ✅ 验签路径(防御)
Agent->>Guard: 提交待签消息, 请求校验
Guard->>Guard: 解码 EIP-712:<br/>读出真实 to / value / chainId
Guard->>Guard: 比对: value==402声明? 地址∈白名单? ≤预算?
Guard-->>Agent: ⚠️ value=10000 ≠ 声明1, 地址不在白名单 → 拒签
Agent->>Evil: 中止, 不签名, 零损失
end
三、防御清单(把盲签堵死)
- 强制解码展示:签名前必须解出 EIP-712 的
to / value / chainId / 消息类型,展示给人或让 Guard 校验。 - 消息类型白名单:只允许
transferWithAuthorization,拒绝approve/permit等无上限授权。 - 收款地址白名单:
to必须 ∈ 已知可信商家地址。 - 金额一致 + 上限:
value必须 == 402 声明金额,且 ≤ 预算硬顶。 - 链/合约核对:
chainId与 USDC 合约地址是预期的。 - 有效期最小化:
validBefore尽量短,减少被利用窗口。 - 独立低额度钱包:即便被骗,损失被账户额度封顶。
四、一句话总结
盲签就是"没看清就签授权";x402 里它能被用来改金额、改收款人、甚至签成掏空钱包的额度授权。 防御核心极简:签前解码 → 比对(类型/地址/金额/链)→ 不符就拒签。这一步把"空白支票"变成"看清金额的支票"。