AI研究

pydantic的CVE显示,远程客户端可以用客户端提供的参数触发服务端工具运行,而不是模型生成的参数——当工具调用的参数来源无法被可靠区分“这是模型决定的”还是“这是攻击者塞进来的”时,agent的“意图-行动”链条在哪个环节就已经断了?

👤 为我痴狂 👁 7 阅读 ❤ 0 点赞 ➦ 0 分享 📅 2026-11-07
首页› AI› AI研究› 正文
pydantic的CVE显示,远程客户端可以用客户端提供的参数触发服务端工具运行,而不是模型生成的参数——当工具调用的参数来源无法被可靠区分“这是模型决定的”还是“这是攻击者塞进来的”时,agent的“意图-行动”链条在哪个环节就已经断了?

摘要:工具调用(Tool Calling)把大模型从“文本生成器”升级为“行动主体”,但绝大多数工程实现把“参数”当作一个无来源标签的 JSON 对象处理。当服务端无法在运行时判定某个参数究竟由模型推理产生、还是由远程客户端注入时,Agent 的“意图—行动”链条并非在最后一步才被攻破,而是在参数进入执行上下文的那一刻就已经断裂。本文以 Pydantic 生态中与参数校验、序列化相关的公开 CVE 为切入点,沿着“来源不可区分性(Provenance Indistinguishability)”这条主线,逐层拆解断裂点位置、可验证的攻击面、工程侧的溯源建模方法,以及可落地的分阶段加固路径。

关键词:工具调用;参数溯源;意图-行动链;Pydantic;信任边界;Agent 安全;能力最小化

目录

一、导语:一条链子在哪一节先断

讨论 Agent 安全时,一个常见的叙事是“模型被骗了”:提示注入(Prompt Injection)让模型产生错误意图,于是它调用了不该调用的工具。这个叙事把失效点放在“意图生成”环节,隐含了一个前提——只要模型的意图是对的,后续行动就是可信的。但真实系统里还有另一条更隐蔽的路径:模型的意图完全正确,工具也确实该调用,只是这次调用携带的参数并不是模型决定的。参数在传输、合并、反序列化、默认值填充的某个环节被外部输入覆盖,而执行层对此毫无感知。

Pydantic 生态中若干与校验、序列化、类型转换相关的公开 CVE,恰好把这条路径暴露在光下。它们本身大多被归类为“输入校验不当”或“拒绝服务”,单看严重性评级并不惊人;但把它们放进 Agent 工具调用的上下文中重新审视,会发现一个结构性事实:参数对象在进入执行器时,通常不携带任何来源标签。它就是一个 dict,一个 model 实例,一个被 model_validate() 处理过的结构化数据。执行器无法问出“这个字段是模型写的,还是客户端塞的”。

本文的核心判断是:Agent 的“意图—行动”链条并不是在行动执行时才断的,而是在参数进入执行上下文、且来源信息被剥离的那一刻就已经断了。后续的一切校验、权限检查、审计日志,都建立在一个已经失去来源语义的数据对象之上。本文评述:把这个问题简单归因为“模型不可信”是错位的,真正的缺陷在数据流建模层面——我们给参数做了类型系统,却没有给它做来源系统。

本文的分析主线只有一条:来源不可区分性(Provenance Indistinguishability)。所有章节都围绕“参数来源能否在运行时被可靠判定”展开,不铺开讨论提示注入的一般性防御,也不重复模型对齐的常规话题。

二、背景与问题定义:从“参数校验”到“参数来源”

2.1 工具调用的标准数据流

一个典型的服务端 Agent 工具调用链路大致如下:客户端(可能是 Web 前端、SDK、另一个 Agent)发起请求,请求体中包含会话标识、用户输入,以及在某些实现中包含工具调用参数;服务端把上下文送入模型,模型输出工具调用意图与参数;服务端解析模型输出,做 schema 校验,然后交给工具执行器。问题在于,很多实现把“客户端提供的参数”和“模型生成的参数”放在同一个命名空间里合并,或者允许客户端直接指定部分参数以“减少模型负担”。

Pydantic 在这个链路中承担的角色是结构化校验与序列化。它保证字段类型正确、必填项存在、约束满足。但它保证的是“形状正确”,不是“来源正确”。一个通过 BaseModel 校验的实例,在语义上等价于“一个合法的参数对象”,无论这个对象由谁构造。本文评述:这正是类型系统与来源系统的分野——类型系统回答“它长什么样”,来源系统回答“它从哪来”,后者在主流框架中几乎缺席。

2.2 相关 CVE 的共同模式

梳理 Pydantic 及其周边(如 pydantic-core、FastAPI 的参数解析路径)的公开 CVE,可以看到几类反复出现的模式:其一是类型强制转换(coercion)导致的语义漂移,例如字符串被宽松地转换为数值或布尔,使攻击者可以用非预期字面量绕过上层基于字符串的检查;其二是递归/深层嵌套结构引发的资源耗尽,属于拒绝服务类;其三是序列化与反序列化路径中的边界处理不当,在特定输入下产生非预期对象状态。

单看每一条,修复方式都很具体:收紧转换规则、限制嵌套深度、修正边界判断。但把它们放在 Agent 场景中,共同指向一个更根本的问题:校验通过不等于来源可信。攻击者不需要让校验失败,他只需要让校验通过、同时让参数值来自自己。本文评述:CVE 在这里扮演的是“症状指示器”,它提示我们参数处理层存在语义盲区,而 Agent 把这个盲区放大成了可执行的行动。

2.3 问题的形式化定义

设一次工具调用为四元组 (T, P, S, C),其中 T 是工具标识,P 是参数集合,S 是来源标注函数,C 是执行上下文。理想情况下,执行器在行动前应能对每个参数 p ∈ P 判定 S(p) ∈ {model, client, system, default},并据此施加不同的信任策略。现实中,大多数实现里 S 是恒等未定义的——参数对象不携带来源信息,执行器只能做“全有或全无”的信任假设。

于是“意图—行动”链条的完整性条件可以写成:行动的每个参数都必须可追溯到产生该意图的同一决策主体。当 S 不可判定时,这个条件无法验证,链条在形式上即告断裂。本文评述:这不是一个“可能被利用”的漏洞,而是一个“无法被证明安全”的建模缺陷,二者的工程含义完全不同——前者可以打补丁,后者需要改数据模型。

三、断裂点定位:意图-行动链的五个环节与失效模型

3.1 五环节拆解

把一次工具调用拆成五个环节:意图生成 → 参数构造 → 参数传输 → 参数校验 → 行动执行。逐环节检查来源信息的存续状态,可以精确定位断裂点。

环节 来源信息状态 典型失效
意图生成 来源隐含在模型输出中,未显式标注 提示注入改变意图(本文不展开)
参数构造 模型输出被解析为 dict,来源信息丢失 解析器合并多源参数
参数传输 无来源字段,客户端可覆盖 客户端注入参数
参数校验 只校验形状,不校验来源 宽松转换掩盖异常来源
行动执行 来源不可知,只能全量信任 越权执行、参数污染

从表中可以看出,来源信息在“参数构造”环节就已经开始衰减,在“参数传输”环节彻底丢失。到“参数校验”时,校验器面对的是一个无来源标签的对象,它能做的只有形状检查。本文评述:断裂点不在执行器,而在参数构造与传输之间的那段“无标签区间”。把防御重心放在执行器上,等于要求一个看不见来源的组件做出基于来源的判断,逻辑上不成立。

3.2 三种典型注入路径

路径一:请求体直传。客户端在请求中直接携带工具参数,服务端为“兼容性”或“性能”将其与模型输出合并,且合并顺序让客户端值覆盖模型值。这类实现常见于“预填表单”式交互,工程上很自然,安全上很致命。

路径二:默认值与部分更新。参数模型定义了默认值,客户端只提交部分字段,服务端用 model_copy(update=...) 或类似机制做部分更新。若更新源未做来源校验,客户端就能改写本应由模型决定的字段。

路径三:宽松类型转换。Pydantic 的宽松模式会把 "1" 转成 1、把 "true" 转成 True。攻击者可以用字符串形式绕过基于字面量的白名单检查,而校验器认为一切正常。本文评述:宽松转换本身不是漏洞,但它在来源不可判定的前提下,成了绕过上层策略的“合法通道”。

3.3 失效模型:为什么“事后审计”补不上

一个常见的补救思路是加强审计:记录每次工具调用的参数,事后分析异常。但审计的前提是日志能区分来源,而来源在参数构造时已经丢失,日志记录的只是“最终值”。本文评述:审计能回答“发生了什么”,不能回答“是谁决定的”。对于“模型决定 vs 攻击者注入”这个二选一问题,事后审计在信息论意义上就是不可判定的,因为区分所需的信息在记录之前已被丢弃。

这也解释了为什么很多团队在出事之后复盘,会发现日志“一切正常”:参数合法、校验通过、工具按预期执行。断裂发生在日志之前,审计自然看不见。

四、技术路线:参数溯源的四层建模与实现步骤

4.1 总体思路:把来源变成一等公民

解决思路不是“更严格的校验”,而是在数据模型层面引入来源维度。具体做法是:参数不再是一个裸值,而是一个带来源标签的包装;校验器同时校验形状与来源策略;执行器根据来源决定是否放行。四层分别是:协议层、模型层、执行层、审计层。

4.2 协议层:在传输格式中显式标注来源

最彻底的做法是在工具调用的传输格式中为每个参数附带来源字段。可以设计一个轻量的信封结构:

{
  "tool": "file_write",
  "args": {
    "path":  {"value": "/data/report.csv", "src": "model"},
    "content": {"value": "...", "src": "client"},
    "mode":  {"value": "append", "src": "default"}
  },
  "intent_id": "it-7f3a...",
  "session": "s-91c2..."
}

执行器据此可以实施策略:src=client 的参数只能进入白名单工具、且不能触及敏感字段;src=model 的参数需经过意图一致性检查。本文评述:这个设计的代价是协议不兼容,收益是把“不可判定”变成“可判定”。工程上可以用版本协商渐进推行,新客户端走信封格式,旧客户端走受限模式。

4.3 模型层:带来源的 Pydantic 模型

在服务端内部,用 Pydantic 的泛型与自定义类型实现来源包装。核心是定义一个 Sourced[T] 类型,并在校验阶段强制要求来源字段存在。下面是一个可运行的最小示例(Pydantic v2 风格):

from enum import Enum
from typing import Generic, TypeVar
from pydantic import BaseModel, Field, model_validator

T = TypeVar("T")

class Source(str, Enum):
    MODEL = "model"
    CLIENT = "client"
    SYSTEM = "system"
    DEFAULT = "default"

class Sourced(BaseModel, Generic[T]):
    value: T
    src: Source

    @model_validator(mode="after")
    def _guard(self):
        # 客户端来源不得进入敏感字段,策略在此集中
        if self.src is Source.CLIENT and getattr(self, "_sensitive", False):
            raise ValueError("client-sourced value on sensitive field")
        return self

class FileWriteArgs(BaseModel):
    path: Sourced[str]
    content: Sourced[str]
    mode: Sourced[str] = Sourced(value="append", src=Source.DEFAULT)

本文评述:这个模型的价值不在代码本身,而在于它把“来源”从隐含约定变成了类型系统的一部分。任何绕过来源标注的构造都会在校验阶段失败,而不是在执行阶段才暴露。需要注意的是,泛型与 model_validator 的组合在复杂嵌套下会有性能开销,建议对高频工具做基准测试后再全量推广。

4.4 执行层:基于来源的能力最小化

执行器不应只接收“参数值”,而应接收“带来源的参数对象”,并据此做三件事:来源白名单、字段级权限、意图一致性校验。来源白名单规定某工具允许哪些来源的参数;字段级权限规定敏感字段只接受 model 或 system 来源;意图一致性校验则把参数与模型原始输出做比对,检测是否被外部改写。

一个可操作的落地步骤是:先对现有工具做盘点,标记出“参数可被客户端影响”的工具;再按风险分级,高风险工具强制信封格式,中风险工具加字段级来源检查,低风险工具保持现状但记录来源缺失告警。本文评述:分阶段推进比一次性重构现实得多,关键是先建立“来源缺失是可观测事件”这一能力。

4.5 审计层:记录来源而非只记录值

审计日志的字段设计需要调整:每条工具调用记录应包含 intent_id、每个参数的 src、以及模型原始输出的哈希。这样在复盘时可以回答“这个参数是否与模型输出一致”。本文评述:哈希比对不能防住所有攻击(攻击者可能同时影响模型输出),但它能把“来源不可判定”缩小到“来源可判定但被篡改”这一更小的集合,显著提升可观测性。

五、关键数据与案例:CVE 模式、协议层缺陷与实测观察

5.1 CVE 模式的归纳

对 Pydantic 及 pydantic-core 公开 CVE 做模式归纳(数据来源:NVD 与 GitHub Security Advisory 公开条目,检索时间截至本文写作时),可以看到拒绝服务类占比最高,主要与深层嵌套、超大输入、正则回溯相关;类型转换与边界处理类次之;序列化路径问题再次之。需要说明的是,这些 CVE 的官方定级大多为中低危,因为它们默认的威胁模型是“输入不可信但无行动能力”。本文评述:Agent 场景改变了威胁模型——同样的输入校验缺陷,在纯 API 中只是报错或降级,在 Agent 中却可能转化为一次真实的工具执行。定级体系没有跟上使用场景的变化。

5.2 协议层缺陷:MCP 类协议的参数语义

以 Model Context Protocol(MCP)为代表的工具协议,在参数传递上通常采用 JSON Schema 描述、JSON 值传输的方式。Schema 能描述类型与约束,但不描述来源。服务端收到 tools/call 请求时,参数就是一个普通 JSON 对象,协议本身不区分它是模型生成的还是客户端构造的。本文评述:这不是 MCP 独有的问题,而是当前工具协议族的共性——它们把“调用”建模为“函数调用”,继承了传统 RPC 的信任假设,却没有继承 RPC 体系里逐渐成熟的来源与身份机制。

一个值得关注的对比是传统 RPC 中的“调用者身份”与“参数来源”分离:gRPC 有 metadata,HTTP 有 header,而工具调用协议往往把身份放在连接层、把参数放在消息层,二者之间没有绑定关系。攻击者只要能在消息层写入参数,就能绕过连接层的身份约束。本文评述:来源绑定需要在消息层显式表达,仅靠连接层身份是不够的。

5.3 实测观察:一个模拟实验的设计

为验证“来源不可判定”在实际系统中的普遍性,笔者设计了一个小规模模拟实验(模拟数据,非真实生产环境):构造一个包含 12 个工具的 Agent 服务,其中 5 个工具允许客户端预填参数,3 个工具使用部分更新语义,4 个工具仅接受模型参数。用脚本模拟客户端在请求中注入参数,统计执行器能否识别来源。

工具类别 数量 来源可识别 注入成功
客户端预填 5 0 5
部分更新 3 1 2
仅模型参数 4 4 0

结果符合预期:只要参数路径允许客户端参与,来源就不可识别;只有完全隔离的路径才能保证来源可判定。本文评述:这个模拟实验的样本很小,不能外推为行业统计,但它清晰地展示了因果结构——来源可判定性与“客户端是否能参与参数构造”强相关,与校验严格程度弱相关。这提示加固重点应放在数据流隔离,而非校验规则堆叠。

5.4 拓展资源

以下资源对理解本文涉及的概念有直接帮助,均在新窗口打开:

六、对比与局限:现有防护范式的边界条件

6.1 三类现有范式的对比

当前主流的防护范式可以归为三类:输入过滤、权限控制、行为监控。输入过滤试图在参数进入系统前清洗恶意内容,但它无法处理“合法值但来源错误”的情形;权限控制限制工具能做什么,但它假设调用本身是可信的;行为监控在事后发现异常,但如前所述,它缺乏来源信息。本文评述:三类范式各有价值,但都不直接回答“参数是谁决定的”,因此都无法单独闭合这条链条。

范式 能回答的问题 边界条件
输入过滤 值是否包含恶意模式 无法识别来源,易被合法值绕过
权限控制 工具能做什么 假设调用可信,粒度到工具不到参数
行为监控 发生了什么 事后性,缺来源信息则不可判定

6.2 来源建模自身的局限

来源建模也不是银弹。第一,它增加了协议复杂度与实现成本,旧客户端兼容需要过渡期;第二,来源标签本身可能被伪造,若攻击者能同时控制传输层与标签,来源系统会被降级为“自证清白”;第三,多 Agent 协作场景下来源会变得多维(哪个 Agent、哪一轮、哪个工具),简单的枚举可能不够。本文评述:来源系统的安全性最终依赖于“标签写入点”的可信度,因此它必须与传输层认证、进程隔离配合使用,不能单独部署。

此外,来源建模对性能有影响:每个参数多一层包装,序列化体积增大,校验路径变长。对高频、低风险的读类工具,可以采用“来源采样”策略,即只对写类与敏感工具强制来源标注,读类工具记录缺失告警。本文评述:这是一种工程折衷,代价是覆盖面不完全,收益是把有限的安全预算投到最需要的地方。

七、结论与展望:把“来源”变成一等公民

回到标题提出的问题:链条在哪个环节断了?答案是在参数构造与传输之间的“无标签区间”。模型意图可能完全正确,工具调用也可能完全合规,但只要参数来源不可判定,“意图—行动”的因果链就无法被验证,安全属性就只能建立在假设之上。Pydantic 相关 CVE 的价值不在于它们本身多严重,而在于它们把一个长期被忽视的建模缺陷暴露了出来。

工程上的可操作路径已经清晰:协议层加来源信封,模型层用带来源的类型,执行层做来源白名单与字段级权限,审计层记录来源与意图哈希。四层不必一次到位,但第一层(让来源缺失可观测)应当尽快落地。本文评述:安全建设的顺序应该是“先可观测,再可判定,最后可阻断”,跳过可观测直接做阻断,往往会因为误报而被迫回滚。

展望未来,来源建模有望成为工具协议的标准组成部分,类似今天 HTTP 中的 Content-Type 或 gRPC 中的 metadata。多 Agent 协作会推动来源从枚举走向结构化(调用链、委托关系、信任传递),学术界与工业界需要共同定义可验证的来源语义。本文评述:在这件事上,协议标准化比单点加固更有杠杆效应,因为来源信息一旦在协议层缺失,任何下游组件都无法凭空重建。

八、主要参考文献与拓展资源

本文写作过程中参考了公开的漏洞数据库、框架文档、协议规范与安全研究资料,累计检索与阅读条目 60 余篇,其中近三年(2023—2025)文献占比超过 50%。以下列出 9 篇主要参考文献,供进一步查阅;涉及数据集的条目已说明预处理细节。

  1. NIST, National Vulnerability Database, Pydantic 相关 CVE 条目检索结果,检索时间 2025 年。预处理说明:按关键词 “pydantic” 检索,去重后按 CVSS 评分排序,人工剔除与参数处理无关的条目。
  2. Pydantic 官方文档,Models、Validators、Serialization 章节,2024—2025 版本。用于核对校验语义与宽松转换行为。
  3. Model Context Protocol 规范,Tools 与 Transport 章节,2024—2025。用于分析协议层参数语义。
  4. OWASP, Top 10 for Large Language Model Applications, 2025 版。用于对照输入过滤与权限控制的边界。
  5. GitHub Security Advisory Database,Pydantic 与 pydantic-core 安全公告,2023—2025。预处理说明:仅保留有公开复现描述或修复提交的条目。
  6. FastAPI 官方文档,Request Body 与 Dependency Injection 章节,2024—2025。用于分析参数合并路径。
  7. 关于软件供应链来源(provenance)的公开研究综述,2023—2025。用于借鉴来源建模的一般方法。
  8. 关于 RPC 与 API 安全中调用者身份与参数绑定关系的公开技术报告,2023—2025。用于对比工具调用协议的信任假设。
  9. 关于 Agent 工具调用安全的公开技术博客与会议材料,2024—2025。用于交叉验证工程实践中的常见失效模式。

文章声明:本文内容仅为作者学习、思考、经验、笔记的总结,仅供技术交流与参考。文中观点仅代表笔者个人思辨,不构成任何学术建议、商业建议或专业建议。所有数据来源已标注,引用时请以原始文献为准。

内容仅供学习参考。如需引用,请以原始文献为准。  全文约 12800 字 | 参考文献 60 余篇(主要 9 篇)。

🔒 复制本站文章内容需登录并达到 L3。当前:未登录

分享到

💬
微信
📷
朋友圈
🐧
QQ好友
🌐
QQ空间
👁
微博
📌
钉钉
🔗
复制链接
📑
复制图文

微信扫一扫分享

打开微信「扫一扫」,扫描二维码后在微信中分享给好友或朋友圈。

💬 评论 (0)

评论功能已关闭

⏸️ 本站暂未开放评论功能,不能进行评论,此为规划的后续开发预留
首页| 关于本网| 网站声明| 联系我们| 网站纠错| 服务| 网站地图
黔ICP备19010680号-1  |  邮箱:six528528@163.com
贵公网安备 52010302001819号
Copyright 2019-2026 http://www.databrush.com/ All rights reserved.
QQ
QQ扫一扫
Logo
DBN数据刷