Skip to content

ToLO 分类体系

ToLO 的 taxonomy 有一个容易误解的地方:它不是按”模型攻击手法”分类,而是按”同一个 LLM-output source 进入了哪类危险 sink”分类。因此七子类共享同一个根因,只是在后果和修复方式上不同。

这一章把 ToLO 分类讲清楚:七子类怎么分、五类防御怎么对应、为什么”分子类”对教学和检测都有用。

这一章给你什么

你将能做到用到的内容
把任意 ToLO 案例归入 ToLO-{Exec, Shell, SQL, Path, SSRF, Deser, Template} 七子类之一Core Patterns
给每个子类挑出类型匹配的修复方案Defensive Patterns
解释为什么”用了 JSON 解析”不算修复 ToLO-SQLDefensive Patterns §C_SAFE^safe-codec
区分 “降低风险” 与 “切断 ToLO”本页 §“通道防御 vs sink 前防御”

如果你已经知道 7 个子类的名字和 5 个 sanitizer 类的对应,可以跳到 Threat Model

你需要先知道什么

读这一章前,你需要熟:

  • S_LLM 是什么(LLM 输出 source 五子集)— 见 先修知识 §5术语表
  • source / sink / sanitizer 的基本含义 — 见 先修知识 §5
  • 至少能写出 3 个具体 sink 函数(如 evalcursor.executeopen)。

如果上面任一不熟,先回去补。

一张图看完全章

ToLO 七子类与五类 C_SAFE 防御对应表 同一 source 分化到七类 sink,每类对应类型匹配的 sanitizer

整章在解释:为什么 S_LLM 是同一个 source、为什么 sink 要分七类、为什么 C_SAFE 必须类型匹配、错配的”防御”为什么不算 sanitizer。

分类原则:source 锚定,sink 开放

这一章的所有名字背后都有同一组关系:

同一个 source class: S_LLM (LLM 输出五子集)
┌─────────┬───────┼────────┬─────────┬────────┐
▼ ▼ ▼ ▼ ▼ ▼
ToLO-Deser ToLO-Exec ToLO-Shell ToLO-SQL ToLO-Path ToLO-SSRF ToLO-Template
(CWE-502) (CWE-94) (CWE-78) (CWE-89) (CWE-22) (CWE-918) (CWE-1336)
│ │ │ │ │ │
└─────────┴───────┼────────┴─────────┴────────┘
不同的 sink: 反序列化 / 代码执行 / shell / SQL / 路径 / URL / 模板
对应不同的 C_SAFE 类型匹配 sanitizer

S_LLM锚点。无论攻击者通过 direct prompt injection、RAG 投毒、工具响应控制还是模型供应链污染影响输出,只要框架把该输出当成可信内部数据传给危险 sink,就进入 ToLO 分析。

S_DANGER开放集合。本站当前使用七个教育性子类:ToLO-{Deser, Exec, Shell, SQL, Path, SSRF, Template}。这些名字帮助读者把 ToLO 映射到熟悉的 CWE family,但不声明 taxonomy 已经穷尽。未来发现新 sink(比如 LLM 输出影响系统资源限制 / IPC / RPC 等),允许扩展。

C_SAFE判断修复是否成立的关键。验证、白名单、参数化、安全编解码和能力门控必须与 sink 类型匹配。错配的 sanitizer 不应被当成真正防御

为什么要分子类

分子类有四个用处。

1. 帮助快速理解后果

  • ToLO-Shell 一看就知道后果与命令执行相关。
  • ToLO-SSRF 一看就知道后果与网络请求相关。
  • ToLO-Path 一看就知道后果与文件读写相关。

这种”名字传递严重程度”的特性,对工程沟通有用。安全人员看到 ToLO-Exec 应该比 ToLO-Template 优先处理。

2. 帮助选择类型匹配的防御

不同 sink 需要不同 sanitizer。错配的 sanitizer 等于没有 sanitizer:

Sink类型匹配的 sanitizer错配的”假 sanitizer”
SQL参数化查询 (%s placeholder)URL 编码、HTML escape
Shell不传 shell=True,用 list 形参SQL escape
路径Path.resolve() + 根目录限制正则过滤 ..(可绕)
URLhost allowlist + scheme 限制quote() 编码
代码收窄到表达式子集 + sandbox黑名单关键字
反序列化safe_loadjson.loads字段长度限制
模板模板字符串固定 + sandbox 模式注释 {{ 字符

分子类的最大价值就是把”修复方向”也分类

3. 帮助写检测规则

sink 端可以复用已有 CodeQL/Semgrep 规则(因为是经典 CWE 的 sink):

  • ToLO-SQL → 复用 SQL injection sink 规则
  • ToLO-Path → 复用 path traversal sink 规则
  • ToLO-Shell → 复用 command injection sink 规则

source 端统一加 LLM output 这一类入口。这样一条 source spec 服务七个子类。

4. 帮助案例归档与统计

公开 CVE 数据库(NVD / GHSA)把 LLM 类漏洞分散归入 CWE-78 / 89 / 94 / 502 / 22 / 918 等。用 ToLO-X 重新归类后,能看出”这些表面不同的漏洞共享同一个 source 类”,从而把现象级聚类做实。

七子类速览(更长展开见 Core Patterns)

ToLO-Deser LLM 输出 → pickle.loads / yaml.load / torch.load (CWE-502)
ToLO-Exec LLM 输出 → eval / exec / compile / PythonREPL (CWE-94)
ToLO-Shell LLM 输出 → os.system / subprocess(..., shell=True) (CWE-78)
ToLO-SQL LLM 输出 → cursor.execute / pandas.read_sql / text() (CWE-89)
ToLO-Path LLM 输出 → open / pathlib.Path.read|write / shutil (CWE-22)
ToLO-SSRF LLM 输出 → requests.get / httpx.get / urllib.urlopen (CWE-918)
ToLO-Template LLM 输出 → jinja2.Template / Environment.from_string (CWE-1336)

五类防御速览(更长展开见 Defensive Patterns)

C_SAFE^schema 形状/类型/枚举约束(Pydantic Literal / Enum / JSON Schema)
C_SAFE^allowlist 枚举或前缀白名单(tool name / SQL 表名 / URL host / 路径前缀)
C_SAFE^parameterized 参数化下游调用(prepared SQL / subprocess list / URL params)
C_SAFE^safe-codec 安全解码器(yaml.safe_load / ast.literal_eval / weights_only)
C_SAFE^capability 能力门控(执行前比对会话权限,与 LLM 输出解耦)

学习重点

先看七子类,建立”同一 source,不同 sink”的直觉。然后看五类防御模式,理解为什么 Pydantic(str)、prompt 分隔符、黑名单过滤、普通日志审计都不足以切断 ToLO 路径。

读完本章后,应当能把一个案例拆成四列:

source transform sink guard
────── ───────── ──── ─────
S_LLM^? parser/... sink^? C_SAFE^? (类型匹配?)

只要这四列能写清楚,后续静态分析规则才有可落地的规格。

最小练习

把下面三条归类:

LLM 输出 "rm -rf /tmp/x" -> os.system(...)
LLM 输出 "../../.env" -> open(...)
LLM 输出 "http://internal.service" -> requests.get(...)

答案分别是 ToLO-ShellToLO-PathToLO-SSRF。三者 source 都是 LLM 输出,差别在 sink。

每条对应的 sanitizer:

路径类型匹配 sanitizer
ToLO-Shell不用 shell=True,改用 subprocess.run(["whitelisted_bin", "--flag", value])
ToLO-PathPath((root / p).resolve()).is_relative_to(root)
ToLO-SSRFURL scheme + host allowlist + 内网 IP block

通道防御 vs sink 前防御

这是 ToLO 防御里最需要内化的区别。

通道防御(channel-side defense)指降低 source 被攻击者影响的概率:

  • Prompt injection 检测器(Lakera Guard、Prompt Guard、Rebuff)
  • RAG 来源签名 / 投毒检测
  • 模型权重签名 / 可信 endpoint
  • prompt 分隔符 / 指令隔离

sink 前防御(sink-side defense)指即使 source 被污染,仍能让污染无法造成程序后果:

  • 五类 C_SAFE

ToLO 的关键洞察:通道防御只降低概率,不切断后果。即使 C1 / C2 防得再好,C3 / C4 / C5 仍可能命中;即使 prompt injection 概率降到 1%,在百万级 query 下绝对数字仍可观。

因此:

修复 ToLO 必须落在 sink 前防御。通道防御只是 defense-in-depth 的补充层。

这个原则会贯穿整个站点。

常见误解

误解 1:“七子类就是七个独立漏洞。”

不是。它们是同一信任模型失效在七类 sink 上的表现。一次修复(收窄 source 端 typed 表达 + sink 端 allowlist)往往同时阻止多个子类。

误解 2:“只要有 Pydantic 就不算 ToLO。”

不一定。Pydantic 只是 C_SAFE^schema 的一种实现,且只在使用 Literal / Enum / Annotated[str, pattern=...] 等约束时才算 sanitizer。自由 str 字段不算

误解 3:“taxonomy 越多越好。”

不一定。新增子类应当有新的 sink 语义(新被保护对象、新类型的 sanitizer 适配),否则用现有子类即可。本站七子类是基于已观察 CVE 的归纳,不声明穷尽 —— 详见 Core Patterns §“分类不闭合性声明”。

误解 4:“分子类只是命名癖,真正修复都一样。”

不一样。ToLO-SQL 的主要修复方向是收窄 SQL 表达空间(表/列 allowlist + 只读账号 + 模板化查询);ToLO-Path 的主要修复方向是根目录约束 + Path.resolve;ToLO-SSRF 的主要修复方向是host allowlist + 内网 block。混用就是错配。

一条完整 ToLO 案例的归类示范

来看 CVE-2023-29374 LangChain LLMMathChain 的归类过程:

该案例的实例
SourceS_LLM^framework — LangChain LLMMathChain 从 LLM 输出中提取的 Python 表达式字符串
TransformLLMMathChain 内部解析 "```python ... ```" block,提取代码,送入 PythonREPL
Sinkexec() 通过 PythonREPL.run() 调用 → ToLO-Exec (CWE-94)
Guard 缺失C_SAFE^safe-codec(没用 numexprast.literal_eval);无 C_SAFE^capability(无 sandbox)
修复方向PR #2943 改用 numexpr.evaluate(expr, global_dict={}, local_dict={...}),把可执行语言从 Python 收窄到数学表达式 → C_SAFE^safe-codec

学完本章后,你应该能对任意 ToLO 案例做出这种”四列拆解 + 修复方向”分析。

下一步阅读