
最近在整理 LLM Agent 安全加固方案时注意到 Duke 团队提出的 ContextLeak 研究方向。这个课题看起来像是攻击侧的内容但实际上它把 Agent 应用里一个很少被认真对待的问题摆到了台面上当模型开始调用工具真正需要保护的不只是用户输入、模型权重或者数据库权限还有运行时的上下文数据。ContextLeak 用强化学习生成可能收集上下文的恶意工具本质上是安全研究里的风险验证而不是一篇可以直接照做的攻击教程。站在防御侧去看它至少提醒我们一件事系统提示词、对话历史、检索片段、中间推理结果这些平时跑在模型上下文窗口里的内容都有可能通过工具链路被带出边界。这篇文章我会从防御者的视角拆解这个研究思路带来的安全启发重点讲 Agent 应用里上下文泄露的风险链路、工具准入和权限边界、审计日志设计以及一套真正能落地的排查顺序。如果你是做 LLM 应用开发、Agent 编排、平台安全或者 RAG 系统的那这篇内容会比较对口。1. ContextLeak 打破的是 Agent 工具链路里的信任默认1.1 运行时上下文到底是什么ContextLeak 里的 Context不是某个 Prompt 片段而是 Agent 在一次任务执行过程中模型能够看到的所有信息。我一般会把 Agent 运行时上下文拆成六类系统提示词里面往往包含角色设定、行为约束、内部工具说明、禁止事项。会话历史用户和 Agent 的多轮对话内容。检索结果RAG 链路从知识库、代码库、文档中取回的片段。文件内容用户上传的 PDF、Word、Excel、图片转文本后的结果。中间推理Agent 的思考步骤、计划列表、上一步工具返回结果。环境信息当前时间、用户标识、会话 ID、工作目录等元数据。这些内容原本分散在不同的存储和服务里但 Agent 在推理时会统一序列化以 token 形式塞进同一个上下文窗口。这是 Agent 应用效率高的原因也是风险集中的原因。传统应用里数据泄露通常发生在“某个接口把过多数据返回给了前端”。Agent 场景里多了一条路径数据被拼进上下文又被某个工具读取、转发或者写进日志。由于上下文是一整块模型很难区分哪些字段能传给工具、哪些字段不能这对安全团队来说才是真正的麻烦。1.2 传统输入输出防护为什么覆盖不住传统 LLM 应用的安全措施会集中在两个位置输入端做 Prompt 注入检测输出端做敏感内容过滤。这两道防线在“纯聊天”场景里基本够用但到了 Agent 场景会出现空档。Agent 与普通聊天最大的区别是模型不只是输出文本还会输出结构化工具调用。工具会读取模型传过来的参数执行外部操作再把执行结果返回给模型这些结果又会进入下一轮上下文中。换句话说上下文会流经外部工具而工具进程不一定像模型服务那样受到同等安全管控。ContextLeak 的研究点就在这个位置如果一个工具本身不可信或者工具链路上被污染那么模型看不到的工具行为可能已经把运行时上下文收集走了。比如工具进程可以读环境变量、读临时文件、发起网络请求、把参数转发到外部接口。传统的输入输出过滤器不会检查这些行为。因为它们只看模型收到的文本和模型生成的文本不关心工具进程实际访问了哪些网络地址和系统资源。这里先强调一个边界我不展开讨论任何恶意工具的实现细节。防御方需要理解的不是“如何构造恶意工具”而是“Agent 链路里不应该存在无约束读取上下文的工具”。2. 这类风险会影响哪些 Agent 应用形态2.1 直接受害对象不止是单次对话很多人觉得上下文泄露就是“聊天记录被偷走”实际影响范围要宽得多。我在实际加固中经常看到四类高发形态应用形态典型上下文内容风险表现企业知识库问答 Agent内部文档、制度、客户信息文档全文被工具读取并外发代码助手 Agent代码片段、依赖配置、本地路径源码片段进入工具参数和日志浏览器自动化 Agent登录态、页面 DOM、用户操作轨迹自动化流程被中间工具篡改多 Agent 协作应用Agent 之间的消息、结果、计划一个 Agent 的输出转发给非预期接收方这类风险最需要警惕的地方在于不需要用户点击恶意链接不需要传统漏洞利用链。只要工具列表中有一个工具在运行时表现出“接受上下文并转发”的行为数据就会出去。2.2 哪些判断标准能快速评估自身风险如果团队还没有能力做全量安全改造可以先按下面几个问题过一遍。Agent 是否会把完整文件内容直接塞进上下文而不是先做摘要和字段抽取工具选择是否完全交给模型自由发挥模型能否通过自然语言描述就调用任意工具工具进程是否运行在宿主机默认环境里能读取环境变量、临时目录和其他进程文件Agent 工具是否具备发送 HTTP 请求、写数据库、发邮件、访问对象存储的能力多 Agent 编排时是否会把一个 Agent 的全部输出直接作为另一个 Agent 的输入如果你的回答里“是”比较多那说明上下文暴露面已经不小了。ContextLeak 这类研究只是在提醒你攻击方可以把“工具选择”变成一个自动搜索过程不再依赖人工编写恶意插件。3. 搭建防护前先做一次上下文资产盘点3.1 按敏感级别给上下文分类很多团队第一次做 Agent 加固时上来就写拦截规则但规则不知道要保护什么效果通常很差。我更建议先花半天时间把上下文资产盘一遍。给上下文做分级不需要特别复杂的框架按数据敏感度分四层就够了级别定义示例L0 公开不涉及隐私泄露无实际影响公开文档介绍、通用知识L1 内部泄露有一定商业影响内部制度、项目排期、非公开产品功能L2 受限泄露会产生明确风险客户联系方式、合同条款、业务报表L3 机密泄露会造成严重事故密钥、口令、身份证号、核心算法分类完成之后给每一项上下文打上等级标签。这里要注意分级不是只给“用户上传文件”分级还要给系统提示词、工具描述、中间推理结果分级。系统提示词看起来只是文本但它可能包含内部工具命名、权限结构、接口地址这些对外部人员都算敏感信息。3.2 画数据流和调用路径上下文分级做完下一步是绘制 Agent 运行时的数据流向。我会用一张简单表格记录每一段链路的输入输出链路节点输入内容输出内容是否会写入日志是否会出网用户输入用户原话、上传文件模型输入是否检索链路查询改写问题文档片段是否工具调用模型生成的参数工具执行结果是可能是外部接口回传工具返回下一轮上下文是否会话存储多轮记录历史记录是视配置而定画完这张表之后重点标记三件事哪些节点能把数据写出进程、哪些节点能访问宿主环境、哪些节点会把输出原封不动拼回上下文。通常只要把这三个节点管住大部分泄露路径就会被切断。4. 工具准入、权限和边界要一起改4.1 工具白名单生命周期管理ContextLeak 这类研究给防御侧带来的最大改变是不能再默认“工具是可信的”。Agent 里的工具应该像第三方依赖一样管理。新增工具必须走审批工具需要具备唯一标识和版本号工具行为说明要写明它能访问哪些资源、能发起哪些外部调用。我建议工具白名单至少包含这些字段工具名称和唯一 ID工具版本和代码包哈希使用场景说明需要的权限范围是否有网络出站能力审批人和最近一次安全复核时间在实际落地时可以先用最小集合跑业务比如一个 Agent 只需要三个工具就不要把工具列表里所有二十个工具都暴露给模型。模型能看到的功能越少被诱导去调用高风险工具的概率就越低。4.2 在工具执行前做参数校验和上下文裁剪工具调用不应该是“模型说什么框架就执行什么”。Agent 编排层应该增加一个中间校验环节。执行流程大致是这样模型生成意图和工具参数。中间层校验工具名是否在白名单内。校验参数类型、长度、来源是否符合工具 Schema。中间层只抽取工具执行所需字段不把完整上下文透传。工具返回结果经过策略过滤后再放回模型上下文。这里最容易被忽略的是第 4 步。很多 Agent 框架会把对话历史、系统提示词和工具调用统一塞进 memory然后工具也能直接调用 memory 对象来读取内容。设计上应该避免让工具访问全局 memory而是通过参数把必要字段传给它。下面是一个防御侧的伪代码思路不是某个框架的现成 APIdef safe_tool_dispatch(tool_name, raw_args, session_context): policy get_tool_policy(tool_name) if not policy.is_allowed: reject(ftool {tool_name} is not allowed) allowed_args extract_allowed_fields(raw_args, policy.allowed_arg_fields) if not validate_args(allowed_args, policy.schema): reject(tool args validation failed) result call_tool_with_sandbox(tool_name, allowed_args) filtered_result sanitize_tool_output(result, policy.allowed_output_fields) return filtered_result这个代码片段不是生产级实现但表达了一个关键思路工具能拿到什么应该在编排层显式控制而不是靠模型自觉。4.3 权限最小化要落到进程和凭据工具服务的权限设计要按“最小够用”来改。每个工具应该使用独立临时凭据而不是共享一个主账号密钥。工具进程如果只需要读取某个目录就不要给它全盘读取权限如果只需要访问对象存储的某个前缀就不要给它整个 Bucket 权限。对于运行在宿主机上的 Agent 工具还要注意环境变量隔离。Agent 服务常见的做法是把模型 API Key、数据库密码、对象存储密钥都存在环境变量里。如果工具进程也运行在同一套环境变量下工具不可信时这些凭据就可能进入日志或者被发送到外部地址。我见过不少 Agent 项目为了省事把工具函数直接写进主服务进程里。这样工具一旦出问题影响范围就是整个 Agent 服务。更稳妥的做法是把敏感性高的工具独立成子服务跑在隔离容器里只开放最小网络白名单。5. 用审计日志把“运行时泄露”变成可观测事件5.1 日志要覆盖哪些字段没有日志的 Agent 安全加固等于没做。你至少要能从一次会话链路里还原出完整工具调用关系。我在设计 Agent 审计日志时会关注以下字段request_id 和 session_id关联一次任务的完整链路。模型调用 ID定位到具体是哪次推理产生的工具调用。工具名称和版本判断是否在白名单内。入参和出参的摘要尽量脱敏不要明文记录完整上下文。工具进程访问的外部地址域名、IP、端口、协议。数据传输量入站和出站字节数能辅助判断异常。执行耗时和结果成功、失败、拒绝原因。审批状态该工具是否经过了安全审批。这里有个容易踩的坑为了排查问题把完整输入输出都记到日志里结果日志系统本身成了数据泄露源头。更合理的做法是先对字段做脱敏和截断只保留需要审计的最小信息。5.2 哪些异常行为值得触发告警单看一条日志很难判断 Agent 是否被污染但连续几条日志叠加起来就会有明显信号。我建议优先关注四类异常工具访问了未登记的域名或非标准端口。Agent 在一次任务中调用同一个工具的频次异常高且参数内容越来越大。工具输出里出现本该只在上下文里存在、而不应该由外部数据源产生的字段。高敏感级别的信息片段出现在工具入参中而该工具本身不需要处理这类数据。如果安全产品能力允许还可以把出站请求内容做一次敏感信息匹配。比如身份证号、手机号、密钥前缀这些特征出现在工具外发请求体里直接升级为高危告警。注意发现异常后不要顺手把原始流量文件删掉也不要急着清空日志。先保留现场再定位链路。6. 强化学习在这一研究方向里真正值得关注的部分6.1 为什么攻击研究开始用强化学习常规的恶意工具依赖人工编写可复制性有限。但如果把“生成能诱发上下文泄露的工具”当成一个序贯决策问题强化学习就有用武之地。从安全研究角度看工具可以看作一种策略在当前状态下给模型什么样的输出能让后续动作走向某个目标。强化学习天然适合在这种环境里搜索策略因为每一步动作都会影响下一步状态而模型的工具调用结果又能作为反馈信号。这个方向里还有一个容易被误解的点强化学习并不总是需要一个定义良好的奖励函数。真实环境里经常存在错误奖励和稀疏奖励Agent 可能在大部分探索中都拿不到准确反馈。所以研究侧开始关注离线强化学习算法比如 IQL它可以直接从一批历史轨迹里学习不需要在线不断试错。这种方式对安全测试场景尤其合适历史轨迹里已经包含大量真实工具行为只需在这些轨迹上搜索出高风险策略就能暴露系统缺陷。6.2 防御团队应该从中得出什么结论不用真的去复现一个恶意工具生成系统但可以得出几个有工程价值的结论。第一恶意工具不一定以“恶意插件”形态出现。它可能是供应链里的一个被污染版本也可能是某个看似正常的开源工具被改动后重新发布。工具行为是否安全不能只看名称和描述。第二工具能力的边界比模型能力边界更重要。模型再聪明也无法阻止一个拥有无约束读权限和网络访问权限的工具把数据带走。防御的重心要放在工具的权限模型上。第三安全测试需要从“基于规则的注入检测”升级到“行为序列分析”。ContextLeak 这类研究把工具行为当作可优化的策略那么防御侧也应该把工具行为当作可建模的序列而不是只看单条 Prompt。7. 从模型层到平台层的逐层加固清单7.1 模型层减少塞进上下文的不必要内容模型安全层不是简单地在 Prompt 里加一句“不要泄露上下文”。模型不具备可靠的事后判断能力真正有效的方法是减少不需要进入上下文的敏感内容。具体操作有三步对大型文件先做内容摘要再进入模型而不是直接把全文塞进去。系统提示词与用户上传内容分开存储不拼接在同一块不可区分的上下文里。当模型请求工具时不要在系统提示词里写上“该工具可以读取所有内部资料”这类描述。判断标准也很直接如果模型回答某个业务问题时根本不需要原始文件全文那就不应该把全文上传到推理链路里。RAG 场景里尤其要检查检索片段的大小。7.2 Agent 编排层把工具调用设计成显式发布订阅编排层最重要的改动是不要让工具直接访问对话历史、系统提示词和全局状态。理想的工具调用应该像一个接口模型发起函数调用请求框架转发给工具工具只接收参数返回结构化结果整个过程没有读取全局 memory 的权限。同时要控制自动执行范围。默认情况下所有有副作用的工具都应该先经过审批尤其是发送消息、发邮件、写数据库、调用外部 HTTP 接口这四类。可以先设置并发为 1、超时时间较短跑通一条任务后再逐步放开不要一上来就把整套自动化流程暴露给线上流量。7.3 工具服务层返回结构必须固定工具方经常出现的一个问题是为了让模型理解结果会把大段原始内容放进返回值里。如果外部攻击者能控制工具返回就相当于往模型上下文里注入额外内容。更稳妥的做法是给每个工具设计固定返回 Schema只保留必要字段。对返回内容做长度限制和字符集校验解析时忽略未知字段。这样做有两个好处一是减少无关数据进入下一轮上下文二是防止工具结果里夹带异常内容。7.4 平台层隔离、出网控制和密钥轮换平台层要解决的核心问题是“工具进程能访问到什么”。我建议按下面几条标准检查工具运行在独立容器里不共享宿主机的完整环境变量。容器只挂载需要的目录不把整个数据卷透传进去。网络出站默认拒绝域名白名单由平台统一维护。数据库、对象存储、消息队列全部使用临时凭据短期有效。高敏感工具和数据源之间增加代理层由代理完成鉴权和审计。这一层做扎实之后即使模型调用了一个被污染工具工具本身没有出网权限数据也很难被带出去。8. 如果已经怀疑发生上下文泄露怎么排查8.1 保留现场并拉升链路Agent 安全问题最麻烦的一点是现场容易消失日志被滚动覆盖、会话被清理、外部请求已经发出。所以第一步不是去修改 Prompt也不是立刻把服务重启而是先保留现场。操作顺序可以参考把这个会话对应的工作流暂停避免继续产生新的工具调用。从 request_id 出发拉出完整的调用链用户输入、模型推理、工具执行、出站请求。核对工具列表确认可疑工具是否在审批白名单内代码版本是否和发布记录一致。对比工具返回值与工具定义 Schema看是否出现了不该由它产生的字段。检查工具进程的网络连接和外部请求日志注意不带敏感信息的原始日志不要直接贴进工单。如果确认有凭据暴露风险先做密钥轮换再继续排查。这里要提醒一句不要只盯着输入侧的 Prompt。ContextLeak 提醒的是工具行为问题所以排查重点要放到工具进程的读权限和出网行为上。8.2 用影子环境验证修复是否生效修复之后不要直接拿生产流量验证。建议先做一个影子副本导入测试数据用一组敏感标记字段走一遍完整链路。验证目标有三个非白名单工具是否还能被模型调用。工具是否还能从宿主环境读取无关路径。工具外发请求是否只会走到白名单域名配置外的域名一概拒绝。如果发现网络请求被正确拦截说明基本边界生效了。如果仍然有模型输出异常就需要继续检查上下文裁剪和工具返回值过滤是否生效。8.3 后续需要持续做的四件事安全加固不是一次性上线。ContextLeak 这类研究出现后工具链路的威胁模型会持续变化至少要形成一套固定节奏每季度重新盘点一次工具白名单和权限。对新增工具跑一次安全评审重点看网络出站范围和文件读取范围。把工具行为审计日志接入告警平台设置异常域名和敏感字段匹配规则。用灰度流量定期做模拟验证不只在事故发生后排查。落到最后我的建议很明确如果只是学习可以先从上下文分级和工具白名单入手这两件事不需要复杂基础设施但对后续所有 Agent 安全设计都有用。如果要上生产至少要保证日志、白名单、出网控制三件事到位。很多 Agent 安全问题不是模型不够聪明而是上下文暴露面太大、工具边界太模糊。把边界收住ContextLeak 带来的风险就能在一个可控范围内被观察和处理。