ToLO Knowledge Base
这里是进入 ToLO Knowledge Base 的起点。它不是论文摘要,而是一张学习地图:先把问题讲成一条能看懂的数据流,再告诉你每个章节解决哪一块知识缺口。
如果你是第一次接触 LLM 应用安全,可以把本页当作”总览 + 导航”:先抓住 ToLO 的核心句子,再去 先修知识 补术语。如果你已经会 Web 安全或静态分析,本页的重点是重新校准一个直觉:LLM 输出不是普通的程序内部值,而是新的 untrusted source family。
本站把 ToLO 作为程序信任边界问题来讲解。信任边界(trust boundary)指数据从”不该直接相信的区域”进入”程序会据此做决定的区域”时的分界线。主线很短:
LLM 输出不是可信内部数据。它可能被攻击者通过 prompt、RAG、工具响应或模型供应链影响。当框架把这些输出送入执行、查询、文件、网络、模板或反序列化 sink 时,它就必须按 untrusted input 处理。读完这页,你应该能回答四个问题:
- ToLO 为什么是信任模型失效,不是一个新的具体漏洞类。
AIMessage.content、tool_call.arguments、structured output 字段为什么是 source。- 七个
ToLO-*子类分别对应哪些传统 sink。 - 后续章节应该按什么顺序读,以及每章解决什么问题。
这里的三个基础词先给出最短定义:
- source:不可信数据进入程序的位置。ToLO 里主要是 LLM 输出、框架包装后的 tool 参数、解析后的字段、structured output 字段和 RAG 内容。
- sink:数据一旦进入就可能产生安全后果的操作,例如执行代码、执行 SQL、读写路径、发起网络请求。
- sanitizer:真正能把某条 source-to-sink 路径变安全的约束或转换。它必须和 sink 类型匹配。
ToLO 用一句话怎么说
ToLO (Trust-on-LLM-Output) 指开发者把 LLM 或 LLM 框架产生的输出字段错误地当作可信内部数据使用;这些字段实际可被攻击者影响,并可能一路流入危险操作。
这句话里有三个重点:
- 信任错位:开发者看到的是
AIMessage、ToolCall、Document、Pydantic model,直觉上像框架内部对象;但对象里的值可能来自攻击者可影响的模型输出或检索内容。 - source 端新颖:危险的 sink 不是新的。
eval、subprocess、SQL、URL fetch、路径读写、模板渲染、反序列化早就是安全社区熟悉的 sink。ToLO 的关键是把 LLM 输出识别为新的 untrusted source family。 - 后果分化:同一个信任模型失效,流到不同 sink 后会表现为不同 CWE:code injection、command injection、SQL injection、SSRF、path traversal、SSTI、insecure deserialization 等。
先看一条完整路径
下面是一个最小的 ToLO 路径:
用户问题 / RAG 文档 / 工具返回 │ ▼PromptTemplate 拼上下文 │ ▼LLM 生成 tool_call.arguments │ ▼OutputParser / JSON parser / Pydantic model │ ▼框架调用工具: run_sql(query) / read_file(path) / fetch_url(url) / eval(code) │ ▼数据库 / 文件系统 / 网络 / 进程ToLO 不要求攻击者直接把 payload 塞进 sink。真正常见的是:攻击者先影响 LLM 输出,然后框架把输出包装成”看起来合法”的工具参数,最后工具把参数交给危险 API。
把上面的路径翻译成静态分析语言:
S_LLM ───────────────► S_DANGER 中间没有类型匹配的 C_SAFES_LLM:LLM-produced 或 LLM-influenceable source。S_DANGER:危险 sink 集合。C_SAFE:能切断对应 source-to-sink 路径的有效 sanitizer。
注意”类型匹配”四个字。json.loads 只能说明文本是合法 JSON,不是 SQL sanitizer;裸 Pydantic str 只能说明字段是字符串,不是 path sanitizer;html.escape 对 shell command 没有意义。
更口语地说,ToLO 不是在问”模型会不会犯错”,而是在问三件更工程化的问题:
- 这个值有没有可能被 LLM 或 LLM 应用上下文影响?
- 这个值最后是否进入了能改变系统状态、访问资源或执行逻辑的 sink?
- 进入 sink 前,有没有一个对该 sink 真正有效的约束?
三题答案如果分别是”是、是、没有”,就进入 ToLO 分析范围。
Source 到底包括什么
本站使用 S_LLM 表示 LLM 相关 source family。它不是只指自然语言回复,而是包括五类在框架中很常见的字段:
| 子集 | 典型字段 | 为什么不可信 |
|---|---|---|
S_LLM^{direct} | response.choices[0].message.content、AIMessage.content | 直接由模型生成,可被 prompt 或上下文影响 |
S_LLM^{framework} | tool_call.arguments、function_call.name、AgentAction.tool_input | 框架包装后的模型决策,常被直接路由到 tool |
S_LLM^{parsed} | json.loads(output) 后的字段、OutputParser 结果 | 解析改变格式,不改变信任级别 |
S_LLM^{structured} | Pydantic / JSON Schema / structured output 字段 | schema 保证形状,不保证语义安全 |
S_LLM^{rag} | Document.page_content、Document.metadata、retriever 返回片段 | RAG 语料可能被上传、索引或第三方内容污染 |
最后一行容易引起疑问:RAG 文档不是 LLM 生成的,为什么也放进 S_LLM? 因为 ToLO 的形式化关注的是经过 LLM 应用栈后被当成可信内容使用的数据。如果 RAG 文档能影响模型输出或被框架作为上下文继续传播,它在安全分析中就需要和 LLM 输出一起建模。
判断 source 时不要只看变量名。safe_answer、parsed_result、validated_model 这些名字都可能让人误判;真正要看的是数据血缘(data lineage):它是否来自模型生成、模型选择、检索内容、工具返回或这些值的派生结果。
攻击者怎么影响它
ToLO 不绑定某一种 prompt injection 技术。本站把”攻击者能影响 LLM 输出”拆成五类通道:
| 通道 | 名称 | 直觉例子 |
|---|---|---|
C1 | direct prompt injection | 攻击者直接在聊天框或 API 参数里给指令 |
C2 | indirect prompt injection | 攻击者把指令放进网页、邮件、文档,被应用读入上下文 |
C3 | RAG 索引投毒 | 攻击者让恶意文档进入知识库或可检索语料 |
C4 | tool response control | 攻击者控制某个工具、MCP server、搜索结果或第三方 API 的返回 |
C5 | model supply-chain compromise | 攻击者影响被加载的模型、fine-tuned 权重或兼容 endpoint |
这五类通道说明了为什么”只修 prompt”通常不够。prompt injection 过滤器可以降低 C1/C2 的概率,但 ToLO 的 sink 前防护仍然要存在,否则 C3-C5 或未来新通道仍可能把不可信值送到危险操作。
通道(channel)和 source 不是同一个概念。C1-C5 解释攻击者怎样影响 LLM 应用里的值;S_LLM 解释代码审计时哪些程序值要标为不可信。一个具体案例可以同时有多个通道,但最终仍会落到某个 source-to-sink 路径上。
Sink 怎么分七类
ToLO 的 sink taxonomy 是开放的,但本站先用七个教育性子类作为主线:
| 子类 | 典型 sink | 对应直觉 |
|---|---|---|
ToLO-Deser | pickle.loads、yaml.load、框架对象反序列化 | LLM 可控结构被当成内部对象恢复 |
ToLO-Exec | eval、exec、compile(...); exec(...) | LLM 生成代码被执行 |
ToLO-Shell | subprocess(..., shell=True)、os.system | LLM 生成命令被 shell 执行 |
ToLO-SQL | cursor.execute(sql)、pandas.read_sql(sql, conn) | LLM 生成查询被数据库执行 |
ToLO-Path | open(path)、Path(path).read_text() | LLM 决定文件路径 |
ToLO-SSRF | requests.get(url)、httpx.post(url) | LLM 决定访问哪个 URL |
ToLO-Template | Environment.from_string(template)、动态模板渲染 | LLM 生成模板或模板上下文 |
这些名字不是为了替代 CWE。更准确地说,ToLO-SQL 仍然会落到 SQL injection 相关 CWE,ToLO-SSRF 仍然会落到 SSRF 相关 CWE。ToLO-* 名称只是提醒你:这一组问题共享同一个 source-side 根因。
读 sink 表时要注意”危险”并不等于”函数名看起来吓人”。requests.get(url) 在普通业务里很正常,但如果 url 来自 tool_call.arguments 且没有 host allowlist,它就成了 ToLO-SSRF 的 sink。open(path) 也是一样:打开固定配置文件没问题,打开模型决定的路径就要检查边界。
什么算有效防御
ToLO 防御的核心不是”让模型更听话”,而是把模型输出限制在程序可控的动作空间内。本站用 C_SAFE 表示能切断路径的防御,主要有五类:
| 类别 | 适合切断什么 |
|---|---|
C_SAFE^{schema} | 严格 schema:枚举、Literal、范围约束、additionalProperties: false 等 |
C_SAFE^{allowlist} | 工具名、路径根目录、URL scheme / host、数据库表名等允许列表 |
C_SAFE^{parameterized} | SQL 参数化、list-form subprocess、HTTP params、sandboxed template |
C_SAFE^{safe-codec} | json.loads 替代 pickle,yaml.safe_load 替代 yaml.load,ast.literal_eval 替代 eval |
C_SAFE^{capability} | 会话级、用户级或工具级 capability gate,只允许当前上下文具备的能力 |
一个 guard 是否有效,取决于它是否和 sink 匹配。例如:
- 对
ToLO-SQL,只允许SELECT、限制表集合、使用只读账号,比让模型”不要写危险 SQL”更可靠。 - 对
ToLO-Path,把模型输出映射到doc_id -> real_path的固定表,比直接接收任意path字符串更可靠。 - 对
ToLO-Shell,让模型选择有限动作枚举,通常比让模型生成整条 shell command 再 quote 更可靠。
所以本站会把”看起来像防御”和”能切断路径的防御”分开讲。日志记录、异常捕获、模型系统提示、输出格式化、普通 JSON 解析都可能有工程价值,但默认不算 C_SAFE;只有它们确实限制了 sink 可接收的危险语义时,才算 ToLO 里的 sanitizer。
它和几个相邻概念的关系
ToLO 与 prompt injection:prompt injection 研究”攻击者怎样让模型输出某种内容”;ToLO 研究”程序怎样处理模型输出”。前者是通道层,后者是 source-to-sink 层。它们相关,但不是同一个问题。
ToLO 与 OWASP LLM 输出处理风险:OWASP 给工程团队一个 broad risk category,提醒大家 LLM output handling 有风险。ToLO 进一步把它落到程序信任模型、source/sink/sanitizer 和静态检测规则上。
ToLO 与传统 CWE:传统 CWE 解释 sink 端危险,比如反序列化、命令注入、SQL 注入。ToLO 不改写这些 CWE,而是说明为什么 LLM 框架里会反复把一个新型 source 送进这些老 sink。
ToLO 与模型安全:ToLO 不研究模型权重是否对齐、是否会被越狱、是否会泄漏训练数据。它研究的是应用代码在拿到模型输出之后,有没有把信任边界守住。
常见误解先排掉
| 误解 | 更准确的理解 |
|---|---|
| ”模型输出是我们服务端生成的,所以是内部数据” | 输出来自服务端调用,但内容受 prompt、RAG、tool 返回和模型供应链影响,不能自动进入可信域。 |
| “用了 JSON / Pydantic 就安全” | 解析和类型校验只说明形状正确;自由字符串字段仍可能是危险 SQL、路径、URL 或代码。 |
| “ToLO 就是 prompt injection” | prompt injection 是影响输出的通道之一;ToLO 是输出被程序错误信任后流入 sink 的路径。 |
| “换成更强模型可以解决” | 更强模型可能减少误生成,但不能替代程序边界上的 allowlist、parameterization 和 capability gate。 |
| “这是旧 CWE 换名字” | sink 端是旧 CWE;ToLO 的新增点是把 LLM 应用栈里的输出字段识别为 source family。 |
这个站点怎么读
站点内容面向两类读者:一类是想快速理解 ToLO 概念的研究读者,另一类是要把概念落到 CodeQL/Semgrep 规则、案例复盘和工程防御的实现读者。页面会保留英文技术标识符,但默认用中文解释。
先从哪里读
如果你不熟悉安全分析术语,先读 先修知识 和 术语表。它们会解释 source、sink、sanitizer、LLM 编排框架、RAG、tool calling 这些基础词。
如果你已经知道 Web 安全或静态分析,可以从 Why ToLO Matters 开始。重点看 ToLO 为什么是”信任模型失效”,不是新的具体漏洞或新的 sink 类型。
学习路径
- 补前置知识:读 先修知识 和 术语表,理解 LLM 编排框架和数据流分析基本词。
- 理解问题定位:读 Background,知道 ToLO 与 prompt injection、OWASP 输出处理风险、传统 CWE 的关系。
- 看懂应用栈:读 LLM Framework Basics,理解模型输出如何变成程序动作。
- 学习分类和防御:读 ToLO Taxonomy,把七类 sink 子类和五类防御模式对应起来。
- 建立威胁模型:读 Threat Model,知道攻击者怎样影响 LLM 输出。
- 落到检测规则:读 Static Analysis 与 CodeQL and Semgrep。
- 用案例巩固:读 Public Case Studies,把每个案例拆成 source、transform、sink、guard。
如果只读一遍,建议按章节顺序走;如果已经熟悉 LLM 应用安全,可以从 ToLO 与已有概念边界 开始,先确认 ToLO 与 OWASP、prompt injection、经典 CWE 的抽象层级差异。
一个贯穿例子
假设一个应用让模型把自然语言转换成 SQL:
用户问题 -> LLM 输出 SQL -> 应用直接 cursor.execute(sql)这里 LLM 输出 SQL 是 ToLO source,cursor.execute 是 sink。如果中间没有只读数据库账号、查询类型限制、表 allowlist 或其他匹配防护,这就是 ToLO 分析要抓住的路径。
更细地看,这个例子至少有三种风险:
- 如果模型能生成
DROP、UPDATE、DELETE,数据库完整性会受影响。 - 如果模型能查询任意表,其他用户数据或系统配置可能泄漏。
- 如果应用把 SQL 错误或查询结果原样回传给模型,后续 agent step 还可能继续扩大影响。
所以 ToLO 分析不会停在”模型输出了 SQL”。它要问:谁能影响这个 SQL? 它流到哪个 sink? 中间是否有和 SQL 语义匹配的 guard? 数据库账号本身有没有 capability 限制?
内容地图
- 先修知识:补齐安全和 LLM 框架基础词。
- 术语表:快速查
source、sink、sanitizer、RAG、capability 等术语。 - Background:说明 ToLO 为什么值得单独命名。
- LLM Framework Basics:解释 LLM 输出如何被包装、解析和执行。
- ToLO Taxonomy:七个 sink 子类与防御模式。
- Threat Model:五类攻击者通道与信任边界。
- Static Analysis:source、sink、sanitizer 与传播规则。
- Public Case Studies:公开 CVE/GHSA 案例复盘。
- CodeQL and Semgrep:规则设计与工具分工。
- Papers:论文消化页与 ToLO 关联。
- External Resources:博客、工具、基准与学习材料。
每个章节都可以按同一套问题读:它在解释 source、sink、sanitizer、attacker channel、trust boundary 中的哪一个? 如果一页没有直接回答这些词,它通常是在提供背景、案例或工具上下文。
读完检查
继续往后读前,确认自己能解释:
- 为什么
tool_call.arguments是 source,即使它是 JSON。 - 为什么 ToLO 不需要发明新的 sink。
- 为什么 structured output 不是自动 sanitizer。
- 为什么 prompt injection 防御不能替代 sink 前的 allowlist、parameterization 或 capability gate。
- 为什么同一个 ToLO 根因可以在 CVE 中表现为不同 CWE。
内容边界
本站只放公开教学内容和已披露案例,不放未披露漏洞、可直接复现攻击的 PoC、真实项目私有分析材料或未经核验的引用结论。
私有研究报告只作为术语和 framing 的校准来源,不会复制到网页。涉及 CVE、GHSA、论文会议和版本范围的页面,在未核验前必须标记 pending 或使用限定语。