智能体支付漏洞:验过报价却签了另一笔
文章指出 x402 支付流程中的检查与使用时序风险:智能体先审核一次报价,支付动作却可能重新请求并签署服务器返回的新条件。若未绑定最终签署的金额、收款方、资产和网络,原有限额检查就可能失效。
AI 深度解读
给 AI Agent 配了单次支付上限,不代表它签名付款时真的会遵守。作者用一个本地测试发现:有的 SDK 检查的是前一次报价,签名时却接受了服务器重新开出的付款要求,连日志都可能记错金额。
这篇值得读的地方,是它把「配置了规则」和「规则确实约束最终动作」分开验证。对接付费 API、给 Agent 接入钱包时,关键问题不是有没有限额参数,而是检查发生在什么时候、检查的对象究竟是谁。
两次请求,可能对应两笔不同的付款
x402 把 HTTP 402 Payment Required 变成支付流程:服务端返回金额、收款人、资产和网络,客户端签署匹配的支付授权,再重试请求。
风险出在常见的两步调用里。Agent 先请求接口,看到「向 0x1111 支付 0.10 USDC」,判断可以接受,再把选中的付款选项交给支付动作。支付动作如果重新请求接口,拿到新的 402,并直接按新要求签名,前面的检查就失去了约束力。
服务器可以把 0.10 改成 0.90,也可以更换收款人或网络。

支付策略必须检查即将签署的付款要求,并将它绑定到此前批准的报价。
作者给出的约束是:金额相同或更低,收款人、资产、网络保持一致,否则不签名。这比「重新检查一次限额」更严格:即使新金额没有超限,收款人变了,也不该自动付款。
作者把它类比为硬件钱包的清晰签名,以及欧洲 PSD2 将认证绑定到金额和收款人的动态关联机制。共同目标都是让最终授权对应付款方确认过的内容。
本地测试发现了哪些检查错位
作者写了一个测试程序,在 127.0.0.1 上模拟不诚实的 x402 卖家:第一次返回正常报价,后续 402 改换付款条件,再解码客户端实际签署的内容。
测试不连接链、不经过 facilitator,也不使用真实资金;除回环地址外的出站连接都会被阻止并记录。每个 SDK 通过适配器按 Agent 的调用方式运行,配置上限为 0.50 USDC。
它覆盖七种情况:诚实报价、涨价到 0.90、更换收款人、同时涨价并更换收款人、更换资产、更换网络,以及首次报价就超限。

按作者对当时公开代码的测试:
- vAPI 检查即将付款的 402,对该报价执行价格上限和预期收款人校验,并从累计额度中预留金额,然后签署同一份报价。授权后若报价改变,它会停止,不自行重试;它还拒绝向非 HTTPS 端点付款。
- ClawRouter 关闭底层库的默认上限,在 x402 客户端的
onBeforePaymentCreation钩子中实施自己的策略。检查对象是马上要签署的付款要求,同时校验收款人、网络和资产白名单,每次拒绝都会说明触发了哪条规则。 - AgentKit 的开发分支在两步流程中检查模型传入的选项,却依据新获取的 402 创建付款。作者观察到报价 0.10、签署 0.90,收款人被替换,以及从 Base Sepolia 切到 Base mainnet 的情况。其自动动作完全没有应用限额。
AgentKit 还有一个更容易误导排查的问题:动作结果报告的是被检查的金额。因此,实际签署 0.90 时,Agent 日志仍可能显示 0.10。
这里必须区分版本。已发布的 AgentKit 0.10.4 没有限额选项,文档将支出控制交给开发者;作者没有把它归为「承诺了限额却执行错误」。这些结果也只是当时三个 SDK 的样本,不能当作今天所有版本的结论。
付款绑定正确,也不等于购买过程正确
按作者描述,当时 x402 规范没有规定上述绑定要求,也没有能抓住这类错误的客户端一致性测试。作者计划向 x402 Foundation 提出这两个问题。
类似缺口还出现在更上一层。Agentic Commerce Protocol 的完成结账调用携带支付凭证,却没有标识买家批准的是哪份订单。作者提出 SEP #325,希望携带 approved_purchase 摘要,在内容不匹配时拒绝完成结账。
摘要可以理解为已批准订单的内容指纹,用来核对最后提交的订单有没有被换掉。它解决的是订单绑定,和底层付款要求的校验处在不同层次。
测试本身也有边界:它验证客户端签了什么,不能证明人看到了什么。如果 Agent 展示给人的内容和它检查的报价不同,客户端即使严格执行,也可能忠实签下一笔用户没想批准的付款。卖家收款后不交付,则又是另一个问题。
我的判断
作者最有价值的做法,是把验证对象推进到了签名结果。只检查传入参数或成功日志,很容易漏掉中间重新请求造成的变化。
vAPI 和 ClawRouter 的实现也说明,防线不必只有一种写法:可以围绕同一份报价完成检查和签名,也可以在创建支付前的钩子里检查最终要求。关键是检查与签名不能各自读取不同的付款条件。
但这份测试没有给出生产事故规模、各 SDK 的完整覆盖率或不同方案的性能对照。它能支持「这些调用路径存在或阻止了报价替换」,不能支持某个 SDK 整体更安全。非 HTTPS 拒付、累计额度和白名单也各有用途,不能仅凭一次通过测试就视为支付安全已经完整解决。
能拿来做什么
- 在 Node 服务或 Agent 工具中接入 x402 时,沿着调用链确认限额检查的位置;若支付动作会重新获取 402,就应核对新要求与批准报价的关系,再决定是否签名。
- 为付款流程加入报价变化测试,至少覆盖金额、收款人、资产和网络,并检查最终签名内容;只断言函数报错或日志显示未超限还不够。
- 让支付动作返回实际签署的金额、收款人、资产和网络,避免前端状态页与审计日志继续展示旧报价。
- 如果界面需要人确认付款,应把展示内容与获批报价关联起来,并单独验证这条链路;原文的客户端测试无法替你证明用户看见了什么。