基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
AIHT
内容
精选全部 AI 动态热点榜AI 日报收藏
分类
模型行业编程产品技术观点开源
更多
Agent 接入
返回首页开发者导航
AIHT
精选 / 资讯详情
9.0
重磅
AI SCORE
技术实践2026-10-22 00:03

两起漏洞揭示 Agent 工具参数的安全边界

dev.to · AI#Agent#安全#参数校验
Editor brief · 编辑速览

文章以 Semantic Kernel 的表达式执行和任意文件路径漏洞说明:模型生成的工具参数必须视为不可信输入。作者提供 AST 白名单与路径规范化校验代码,并用小规模自建样本展示其相对字符串拦截的优势。

文章思维导图
Knowledge map
拖拽缩放
Full translation

完整中文译文

模型读了一封邮件、一个网页,就可能被其中的指令带偏。真正需要追问的是:它接下来调用工具时,能不能把一段表达式送进 eval(),或者把文件写到任意目录?

这篇文章借两个 Semantic Kernel 漏洞给出的结论很明确:模型生成的参数,应当像互联网上提交的表单一样被检查。作者用一组小样本演示了黑名单和白名单的差别,也划清了边界:参数校验能限制破坏范围,却不能保证 Agent 的行为符合用户意图。

两个 CVE,暴露的是同一条信任链

原文讨论的两个漏洞均在 2026 年 2 月披露,CVSS 评分都是 9.9。Microsoft Defender Security Research 团队于 2026 年 5 月 7 日发表分析。原文没有列出受影响版本和修复版本,不能据此判断某个具体安装是否安全。

CVE-2026-26030 出在向量存储的过滤逻辑:程序把字符串拼成 Python lambda,再交给 eval()。其中的搜索参数由模型生成,插入代码前没有得到充分校验。攻击者可以通过提示注入影响模型输出,让参数逃出原来的字符串位置。

原实现有黑名单,但攻击者能沿着 Python 类型体系访问进程中的其他对象,不必直接写出被禁止的词。Microsoft 描述的修复包含 AST 节点白名单、危险属性名黑名单等多层检查。

CVE-2026-25592 则从工具暴露开始。内部辅助方法 DownloadFileAsync 被加上 [KernelFunction],成了模型可调用的工具,还允许模型指定本地目标路径。攻击链先向沙箱写脚本,再向 Windows Startup 目录写文件,使代码在下次登录时运行。

公告提供的临时措施,是用函数调用过滤器检查两个文件传输函数的参数,确保 localFilePath 位于白名单中。

一个是字符串拼接代码,一个是路径未经约束。提示注入负责把危险参数送进来,漏洞则让参数获得了执行代码、越界写入的能力。

模型可以提出操作,但不能替系统批准操作。

原文引用 Unit 42 在 2026 年 3 月的报告:其收录案例中,14.2% 涉及数据破坏意图,85.2% 使用社会工程式越狱。这是该报告的样本分布,不能直接当成所有 Agent 流量的攻击比例,但足以说明外部文本需要按不可信输入处理。

表达式为什么要检查语法,而不是搜索危险词

作者先做了一个字符串黑名单,拦截 import、exec、eval、os.、system、open(。结果,14 个对抗性过滤表达式中有 11 个通过。

原因在于,危险行为不只有一种写法。属性访问、类型继承关系、传给 getattr 的拼接字符串,都可能避开这些词。作者在干净的 Python 3.12.10 解释器中演示,从空元组出发便能枚举到 164 个类;应用加载更多库后,可达对象还会增加。

AST(抽象语法树)白名单反过来规定:这里只接受行变量 x、指定字段、比较与布尔运算,以及指定类型的常量。

下面这段代码解决的是「过滤表达式允许长成什么样」:

import ast

ALLOWED_FIELDS = {"price", "category", "rating"}
ALLOWED_NODES = (ast.Expression, ast.BoolOp, ast.And, ast.Or, ast.Not, ast.UnaryOp,
                 ast.Compare, ast.Eq, ast.NotEq, ast.Lt, ast.LtE, ast.Gt, ast.GtE,
                 ast.Constant, ast.Name, ast.Load, ast.Attribute)

def gate_filter(expr: str) -> bool:
    try:
        tree = ast.parse(expr, mode="eval")
    except SyntaxError:
        return False
    for node in ast.walk(tree):
        if not isinstance(node, ALLOWED_NODES):
            return False                       # 禁止调用、lambda、下标、推导式和 f-string
        if isinstance(node, ast.Name) and node.id != "x":
            return False                       # 只允许行变量
        if isinstance(node, ast.Attribute):
            if not (isinstance(node.value, ast.Name) and node.value.id == "x"
                    and node.attr in ALLOWED_FIELDS):
                return False                   # 只允许已知字段,禁止 x.__class__
        if isinstance(node, ast.Constant) and not isinstance(node.value, (int, float, str, bool)):
            return False
    return True

重点是 node.attr in ALLOWED_FIELDS:允许属性节点还不够,必须限制属性属于谁、名字是什么。

作者的测试中,这道检查拦住了全部 14 个对抗表达式,同时接受全部 4 个正常过滤条件。

两种过滤检查允许通过的正常与对抗表达式数量

但通过 AST 检查,不等于查询符合权限要求。原文中的 "' or 1 or '" 会解析成一个字符串常量,作为条件始终为真,可能返回全部记录。它没有执行代码,却仍可能造成数据越权。行级授权必须另外落实。

作者也建议,即使保留 eval(),仍应清空 builtins、限制作用域。能把过滤条件改成「字段、操作符、值」的结构化数据,并自行构造判断逻辑,就优先避免 eval()。

路径要按目录关系检查,工具要默认拒绝

文件路径的黑名单同样容易漏。只检查 ..,挡不住 /etc/passwd;检查 startswith(root),又会把 /sandbox/session-42-evil/x 当成 /sandbox/session-42 的内部路径。

作者用下面的代码比较这几种检查:

import posixpath

ROOT = "/sandbox/session-42"

def _full(p: str) -> str:
    return posixpath.normpath(p if p.startswith("/") else posixpath.join(ROOT, p))

def naive_dotdot(p):     return ".." not in p
def naive_startswith(p): return _full(p).startswith(ROOT)
def canonical_gate(p):   return posixpath.commonpath([ROOT, _full(p)]) == ROOT

关键在最后一行:commonpath 比较路径组件之间的包含关系。测试包含 4 个合法路径和 7 个非法路径;.. 检查与前缀检查各漏过 2 个,规范化后的包含检查漏过 0 个。

不同路径检查允许通过的正常与对抗路径数量

这仍只是字符串层面的检查。posixpath.normpath 和 commonpath 不解析符号链接,真实文件系统还需要考虑 os.path.realpath 或 Path.resolve();原文也提到 pathlib 的 Path.relative_to 可用于包含判断。.NET 修复则先使用 Path.GetFullPath() 规范化路径。

检查之后如何使用,也决定边界是否有效。out/..%2f..%2fetc/x 在演示中是合法的字面目录名,如果后续组件又做 URL 解码,先前检查的就不是最终使用值。

校验、授权和实际操作,必须针对同一个最终值。

作者进一步主张,把检查放进所有工具调用必经的统一入口。示例用 POLICY 为 search_products.filter 配置 gate_filter,为 write_report.path 配置 canonical_gate,未列出的工具一律拒绝。这样,误注册的辅助方法也无法直接被调用。

CI 中还能检查 set(TOOLS) == set(POLICY),让新增工具却未配置策略的变化尽早暴露。

我的判断

文章最有价值的地方,是把提示注入问题落到了程序员熟悉的输入边界:参数类型、表达式语法、路径范围、工具权限。Prompt 中写「禁止越界」,可以辅助模型行为,但安全判断需要由工具入口的确定性代码承担。

不过,这些数字只能证明示例中的失败方式。14 个表达式、7 个路径都由作者编写,而且大部分表达式刻意避开黑名单词汇。作者自己也明确说,这不是基准测试。没有生产流量、独立攻击集或长期运行数据,不能把「0 个漏过」解释成已经证明安全。

路径演示尤其不能直接照搬成生产方案。它采用 POSIX 路径语义,没有覆盖完整 Windows 环境;解析符号链接后,也仍要考虑检查与使用之间文件系统是否变化。原文提醒了检查与使用漂移,但没有给出完整的并发文件操作实现。

统一入口的方向我赞同,但示例 invoke 只检查策略表里列出的参数,再把整个 kwargs 传给工具,并不等于验证了所有参数。生产实现还得明确多余参数、类型错误以及跨参数约束如何处理。

此外,允许写入沙箱不代表允许执行其中的脚本。文件范围、执行权限、行级授权各自解决不同问题,需要共同约束工具的实际能力。

能拿来做什么

  • 给日常 AI 编程 Agent 清点真实注册的工具,重点查内部辅助方法是否意外暴露,并在调用入口配置默认拒绝的工具白名单。
  • 如果搜索工具接受模型生成的过滤条件,优先改成结构化的字段、操作符和值;确需表达式时,再限制 AST,并独立执行数据授权。
  • 为文件工具统一完成路径解析、范围检查和实际使用,检查中间层是否还会解码、展开或重新拼接路径,并针对真实文件系统处理符号链接。
  • 把原文涉及的类型链访问、同名前缀目录、绝对路径和编码路径加入回归样本;记录被拒绝的工具名与参数名,再持续补充真实绕过案例。
Original source

本文由 AI 翻译整理自 dev.to · AI,原文版权归原作者所有。

阅读英文原文
上一篇
DeepSeek 更新后,Agent 成本该怎么算
下一篇
流式工具调用被截断后,为何必须拒绝执行

资讯速览

dev.to · AI · 2026-10-22 00:03

AI 评分 9.0 / 10

相关标签

#Agent#安全#参数校验

继续阅读

  • MemT 探索原生记忆,声称缓存缩减66倍
  • DeepSeek 更新后,Agent 成本该怎么算
  • 流式工具调用被截断后,为何必须拒绝执行
  • 英伟达扩展本地推理集群与编程模型
  • AI 生成界面的无障碍漏洞怎么查