Skip to content

ToLO Knowledge Base

这里是进入 ToLO Knowledge Base 的起点。它不是论文摘要,而是一张学习地图:先把问题讲成一条能看懂的数据流,再告诉你每个章节解决哪一块知识缺口。

如果你是第一次接触 LLM 应用安全,可以把本页当作”总览 + 导航”:先抓住 ToLO 的核心句子,再去 先修知识 补术语。如果你已经会 Web 安全或静态分析,本页的重点是重新校准一个直觉:LLM 输出不是普通的程序内部值,而是新的 untrusted source family。

本站把 ToLO 作为程序信任边界问题来讲解。信任边界(trust boundary)指数据从”不该直接相信的区域”进入”程序会据此做决定的区域”时的分界线。主线很短:

LLM 输出不是可信内部数据。
它可能被攻击者通过 prompt、RAG、工具响应或模型供应链影响。
当框架把这些输出送入执行、查询、文件、网络、模板或反序列化 sink 时,
它就必须按 untrusted input 处理。

读完这页,你应该能回答四个问题:

  • ToLO 为什么是信任模型失效,不是一个新的具体漏洞类。
  • AIMessage.contenttool_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 框架产生的输出字段错误地当作可信内部数据使用;这些字段实际可被攻击者影响,并可能一路流入危险操作。

这句话里有三个重点:

  • 信任错位:开发者看到的是 AIMessageToolCallDocument、Pydantic model,直觉上像框架内部对象;但对象里的值可能来自攻击者可影响的模型输出或检索内容。
  • source 端新颖:危险的 sink 不是新的。evalsubprocess、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_SAFE
  • S_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 不是在问”模型会不会犯错”,而是在问三件更工程化的问题:

  1. 这个值有没有可能被 LLM 或 LLM 应用上下文影响?
  2. 这个值最后是否进入了能改变系统状态、访问资源或执行逻辑的 sink?
  3. 进入 sink 前,有没有一个对该 sink 真正有效的约束?

三题答案如果分别是”是、是、没有”,就进入 ToLO 分析范围。

Source 到底包括什么

本站使用 S_LLM 表示 LLM 相关 source family。它不是只指自然语言回复,而是包括五类在框架中很常见的字段:

子集典型字段为什么不可信
S_LLM^{direct}response.choices[0].message.contentAIMessage.content直接由模型生成,可被 prompt 或上下文影响
S_LLM^{framework}tool_call.argumentsfunction_call.nameAgentAction.tool_input框架包装后的模型决策,常被直接路由到 tool
S_LLM^{parsed}json.loads(output) 后的字段、OutputParser 结果解析改变格式,不改变信任级别
S_LLM^{structured}Pydantic / JSON Schema / structured output 字段schema 保证形状,不保证语义安全
S_LLM^{rag}Document.page_contentDocument.metadata、retriever 返回片段RAG 语料可能被上传、索引或第三方内容污染

最后一行容易引起疑问:RAG 文档不是 LLM 生成的,为什么也放进 S_LLM? 因为 ToLO 的形式化关注的是经过 LLM 应用栈后被当成可信内容使用的数据。如果 RAG 文档能影响模型输出或被框架作为上下文继续传播,它在安全分析中就需要和 LLM 输出一起建模。

判断 source 时不要只看变量名。safe_answerparsed_resultvalidated_model 这些名字都可能让人误判;真正要看的是数据血缘(data lineage):它是否来自模型生成、模型选择、检索内容、工具返回或这些值的派生结果。

攻击者怎么影响它

ToLO 不绑定某一种 prompt injection 技术。本站把”攻击者能影响 LLM 输出”拆成五类通道:

通道名称直觉例子
C1direct prompt injection攻击者直接在聊天框或 API 参数里给指令
C2indirect prompt injection攻击者把指令放进网页、邮件、文档,被应用读入上下文
C3RAG 索引投毒攻击者让恶意文档进入知识库或可检索语料
C4tool response control攻击者控制某个工具、MCP server、搜索结果或第三方 API 的返回
C5model 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-Deserpickle.loadsyaml.load、框架对象反序列化LLM 可控结构被当成内部对象恢复
ToLO-Execevalexeccompile(...); exec(...)LLM 生成代码被执行
ToLO-Shellsubprocess(..., shell=True)os.systemLLM 生成命令被 shell 执行
ToLO-SQLcursor.execute(sql)pandas.read_sql(sql, conn)LLM 生成查询被数据库执行
ToLO-Pathopen(path)Path(path).read_text()LLM 决定文件路径
ToLO-SSRFrequests.get(url)httpx.post(url)LLM 决定访问哪个 URL
ToLO-TemplateEnvironment.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.loadast.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 规则、案例复盘和工程防御的实现读者。页面会保留英文技术标识符,但默认用中文解释。

先从哪里读

如果你不熟悉安全分析术语,先读 先修知识术语表。它们会解释 sourcesinksanitizer、LLM 编排框架、RAG、tool calling 这些基础词。

如果你已经知道 Web 安全或静态分析,可以从 Why ToLO Matters 开始。重点看 ToLO 为什么是”信任模型失效”,不是新的具体漏洞或新的 sink 类型。

学习路径

  1. 补前置知识:读 先修知识术语表,理解 LLM 编排框架和数据流分析基本词。
  2. 理解问题定位:读 Background,知道 ToLO 与 prompt injection、OWASP 输出处理风险、传统 CWE 的关系。
  3. 看懂应用栈:读 LLM Framework Basics,理解模型输出如何变成程序动作。
  4. 学习分类和防御:读 ToLO Taxonomy,把七类 sink 子类和五类防御模式对应起来。
  5. 建立威胁模型:读 Threat Model,知道攻击者怎样影响 LLM 输出。
  6. 落到检测规则:读 Static AnalysisCodeQL and Semgrep
  7. 用案例巩固:读 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 分析要抓住的路径。

更细地看,这个例子至少有三种风险:

  • 如果模型能生成 DROPUPDATEDELETE,数据库完整性会受影响。
  • 如果模型能查询任意表,其他用户数据或系统配置可能泄漏。
  • 如果应用把 SQL 错误或查询结果原样回传给模型,后续 agent step 还可能继续扩大影响。

所以 ToLO 分析不会停在”模型输出了 SQL”。它要问:谁能影响这个 SQL? 它流到哪个 sink? 中间是否有和 SQL 语义匹配的 guard? 数据库账号本身有没有 capability 限制?

内容地图

每个章节都可以按同一套问题读:它在解释 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 或使用限定语。