Trust Boundaries
信任边界(trust boundary)是数据跨过不同信任级别的位置。ToLO 的核心问题是:攻击者可影响的数据通过 C1-C5 进入 S_LLM,然后在没有类型匹配检查的情况下跨过工具边界或执行边界。
ToLO 威胁模型核心是:不可信数据通过五种通道之一进入 LLM 输出,再被框架代码错误地当作可信数据处理。本节先列攻击者能力 + 通道 + 被保护对象 + 五条边界,再给出判断 ToLO 的三问法和它的实际应用例子。
威胁模型的作用是约束讨论范围。ToLO 不假设攻击者已经控制服务器,也不把所有模型安全问题都纳入范围;它只问一个远程低权限攻击者能否影响 LLM 输出,并让该输出驱动敏感程序操作。
这一页的结构
- 先修概念:边界 = 信任级别变化的位置
- 攻击者能力(有什么 / 没什么)
- 五个攻击者通道(简要;详细在 attacker-channels)
- 六类被保护对象
- 数据流上的五条边界
- 边界检查表(实战清单)
- 判断 ToLO 的三问法 + 应用例子
- 防御落点
- 不在本章范围的内容
- FAQ + 自测
先修概念:边界是什么
边界是”信任级别发生变化”的位置。
举例:
- 用户输入进入服务器时是边界(不可信 → 应用处理域)。
- 应用要把数据写入数据库时是边界(应用内部 → 持久化层)。
- 应用要执行 shell 命令时是边界(应用内部 → OS 解释器)。
每条边界的关键问题是:穿过边界前,数据是否被相应的检查 / 转换 / 隔离?
ToLO 的关键边界
ToLO 主要关心两条:
- 模型边界:模型输出从 token 变成程序数据。这一刻,数据第一次成为程序可见值。
- 执行边界:程序数据被拿去执行真实操作(shell / SQL / open / requests / eval / pickle / Template)。
如果模型边界没把输出标成不可信,执行边界又没检查权限,就形成 ToLO。
§1 攻击者能力
ToLO 默认攻击者是远程低权限攻击者。低权限不代表无能力,而是说攻击者只能通过应用暴露的正常交互面或外部依赖影响系统。
攻击者有的能力
| 攻击者有的能力 | 对应通道 | 说明 |
|---|---|---|
| 正常使用聊天、HTTP API、agent 接口、上传入口 | C1 | 可以提交任意文本或普通用户可提交的文件 |
| 发布网页、文档、邮件、issue、评论、工单等可被读取的内容 | C2 | 目标 agent 读取后才会生效 |
| 向被索引语料库写入或影响若干条目 | C3 | 包括公共知识库、内部 wiki、用户上传文档、同步数据源 |
| 控制某个外部工具或服务的返回 | C4 | 包括第三方 plugin、MCP server、搜索/API provider、被劫持的数据源 |
| 发布、替换或伪装应用加载的模型 artifact / endpoint | C5 | 包括 fine-tune、adapter、本地权重、兼容 API endpoint |
攻击者没有的能力
| 攻击者没有的能力 | 为什么排除 |
|---|---|
| 服务器 shell 或容器 exec 权限 | 如果已有执行权限,许多 sink 风险不再需要 LLM 链路解释 |
| 目标系统源代码读取权限 | ToLO 路径应能从公开功能、API 文档或用户界面触达 |
| 文件系统直接读写权限 | 文件读写必须通过应用的 tool、worker 或框架代码发生 |
| source-of-truth 数据库直连权限 | 数据库影响必须通过应用连接和 LLM 驱动的查询发生 |
| 受害用户 credential 或 session | 跨用户影响必须由应用授权边界失败造成,不能直接假设冒用账号 |
| 修改应用代码或部署配置的管理员权限 | 除 C5 中的上游模型供应链外,本章不假设攻击者是运维管理员 |
这意味着 ToLO 不是”本地恶意用户调用危险 API”问题。攻击者的入口仍是应用暴露的正常交互面,只是 LLM 编排层把这些交互转化成了更强的程序能力。
一个重要限制:不需要源码
ToLO 的很多路径来自公开功能本身:聊天、上传文档、连接工具、触发 agent workflow。攻击者不需要读源码就能尝试这些路径——公开 API 文档、用户手册、demo 视频通常足够。
这也是 ToLO 检测的紧迫性来源:当攻击者不需要源码,公开 SaaS / 开源 agent 都是受攻击面。
§2 五个攻击者通道(简要)
本论文不预设单一通道,把”LLM 输出对攻击者可影响”作为抽象前提,承认可经以下任一通道实现:
| 通道 | 简述 | 直接控制点 | 主要限制 |
|---|---|---|---|
| C1 direct prompt injection | 攻击者直接发送 prompt | 当前会话输入 | 不控制模型供应链、RAG 历史索引、第三方工具 |
| C2 indirect prompt injection | 攻击者发布内容,被 agent 读取 | 外部内容 | 需要用户或 agent 读取该内容 |
| C3 RAG indexing poisoning | 攻击者污染被索引语料 | RAG chunk / metadata | 需要检索命中,不保证所有查询受影响 |
| C4 tool response control | 攻击者控制 tool 返回 | tool response | 需要 agent 调用该工具 |
| C5 model supply-chain compromise | 模型 artifact / endpoint 被污染或替换 | 模型输出 | 仍不能越过独立 sink guard |
ToLO 的结论独立于具体通道。即使 prompt injection 被完美防御,C3 / C4 / C5 仍可触发 ToLO。
因此案例复盘中不需要证明某个特定 prompt injection 技巧永远有效。只要能说明 LLM 输出存在攻击者可影响通道,并且该输出进入危险 sink,就可以进入 ToLO 判定。
详细展开(每个通道的可达 source 子集、触发条件、防御对应)见 Attacker Channels。
§3 六类被保护对象
ToLO 关心攻击者影响程序能做的事,因此被保护对象按”能做哪些坏事”分:
| 资产 | 需要保护的性质 | 典型危险操作 | 常见 ToLO 子类 |
|---|---|---|---|
| 进程完整性 | 模型输出不能变成任意代码或对象构造 | eval、exec、不安全反序列化、动态模板 | ToLO-Exec, ToLO-Deser, ToLO-Template |
| 文件系统 | 模型输出不能选择未授权路径或覆盖关键状态 | 文件读写、复制、移动、删除 | ToLO-Path |
| 网络出口 | 模型输出不能选择未授权 host、scheme 或内部资源 | HTTP fetch、webhook、API caller | ToLO-SSRF |
| 数据库 | 模型输出不能决定未授权查询结构或写操作 | SQL/Cypher 执行、ORM raw query | ToLO-SQL |
| 密钥与凭据 | secret 不应被读取、拼接进请求或展示给错误用户 | 环境变量、配置、token store | 任意子类的下游放大 |
| 其他用户数据 | tenant/user A 的数据不应被 B 的交互读取或修改 | 文档读取、导出、消息发送、数据库查询 | 任意子类 |
初学者可以把这六类理解成”不能让模型输出随便碰的东西”。只要 LLM 输出能影响这些对象,就必须有明确边界。
注意”密钥与凭据”和”其他用户数据”通常不是 sink 本身,而是下游放大:
LLM 输出 → open(攻击者控制路径) → 读到 /app/.env → exfiltrate ↑ 凭据外泄是后果写报告时,sink 子类填实际 sink(这里是 ToLO-Path),后果填被保护对象(凭据外泄)。
写案例时建议分开记录:
| 字段 | 应填什么 |
|---|---|
| 通道 | C1-C5 哪条路径影响了 S_LLM |
| source | 哪个 S_LLM 子集被影响 |
| sink | 进入了 ToLO-{Deser,Exec,Shell,SQL,Path,SSRF,Template} 哪一类 |
| sanitizer | 是否存在类型匹配的 C_SAFE |
| 资产影响 | 进程、文件、网络、数据库、凭据、其他用户数据哪类受影响 |
§4 数据流上的五条边界
把整条 LLM 应用栈按信任级别切五段:
外部/低可信输入 │ ① 输入边界 ▼应用上下文与 prompt 构造 │ ② 模型边界 ▼LLM 输出 │ ③ 解析边界 ▼结构化对象 / tool call │ ④ 工具边界 ▼tool 参数 │ ⑤ 执行边界 ▼真实操作: shell / SQL / file / network / deser / template| 编号 | 名字 | 信任变化 | 应检查什么 | 常见误区 |
|---|---|---|---|---|
| ① | 输入边界 | 外部内容进入应用 | 来源、tenant、上传者、文档可信级别 | 把网页、邮件、用户文档直接当系统指令材料 |
| ② | 模型边界 | prompt 进入模型,输出返回应用 | provenance、输出默认不可信、失败模式 | 认为”来自模型”比”来自用户”更可信 |
| ③ | 解析边界 | 文本变成 JSON、对象、字段 | schema、额外字段、枚举、长度、格式 | 只因 JSON 可解析就认为安全 |
| ④ | 工具边界 | 对象变成 tool 名和参数 | tool allowlist、resource ID、当前用户 capability | 让模型选择任意工具或扩大权限 |
| ⑤ | 执行边界 | 参数进入真实 sink | 参数化、safe codec、sandbox、allowlist、RBAC | 把自然语言确认当作真实执行确认 |
边界越靠后,后果越具体:
- ① 输入边界失守:模型可能被诱导
- ② 模型边界:不可控
- ③ 解析边界失守:出现意外字段类型
- ④ 工具边界失守:危险参数被传入
- ⑤ 执行边界失守:RCE / SQL injection / SSRF / 文件读写 → 真正的 ToLO 后果
ToLO 的研究重点落在 ③/④/⑤:污染输出如何从模型边界一路走到工具边界和执行边界。
边界例子:网页 → 模型 → URL → 请求
来看一段具体数据流:
[evil.example/blog.html] ──①──► [agent.fetch_url] ──②──► [LLM 上下文] │ ③ ▼[requests.get(url)] ◄──⑤── [tool args] ◄──④── [LLM 输出 tool_call]这里至少有四个边界:
- 网页内容进入系统:输入边界 ①。
- 文档进入 prompt:模型上下文边界(②)。
- LLM 输出 URL:模型边界 → 解析边界 ③ → 工具边界 ④。
- URL 被请求:执行边界 ⑤。
ToLO 重点检查最后两步:
- ④ 模型输出 URL 是否被当成可信 URL?
- ⑤
requests.get之前有没有 host allowlist、内网阻断和 capability?
如果都没有,这是教科书级别 ToLO-SSRF。
§5 边界检查表
输入边界
- 每段外部内容是否保存 provenance,例如来源、上传者、tenant、时间、hash?
- RAG chunk 是否区分权威文档、用户上传、外部网页和第三方同步内容?
- 低可信内容是否只能作为资料,不能提升为系统指令?
- 外部内容是否能跨用户或跨 tenant 进入 prompt?
模型边界
- 应用是否把 LLM 输出统一标记为 untrusted?
- 结构化输出字段是否保留来源信息,而不是变成”普通内部对象”?
- 模型拒答、错误、tool call 和普通文本是否走不同处理路径?
- C5 场景下,模型 artifact、adapter、endpoint 是否有版本和 hash 记录?
解析边界
- schema 是否限制
additionalProperties、枚举、长度、格式和值域? str字段是否被误认为已清洗?- 解析失败是否 fail closed,还是回退到自由文本执行路径?
- 嵌套字段、metadata、tool arguments 是否同样按
S_LLM处理?
工具边界
- tool 名是否来自固定 allowlist,而不是模型自由生成?
- 参数是否映射到资源 ID,再由服务器查真实路径、URL、表名或对象?
- 当前会话 capability 是否由服务器授予,且不可由 LLM 修改?
- 人工确认是否展示将执行的精确动作,而不是模型摘要?
执行边界
- SQL 是否参数化或限制为预定义查询模板?
- shell 是否避免自由命令字符串,并在 sandbox 中运行?
- URL 是否校验 scheme、host allowlist、内网阻断和 redirect 策略?
- 文件路径是否经过 canonicalization、root allowlist 和权限检查?
- 反序列化是否使用 safe codec,避免任意对象构造?
- 模板是否固定模板字符串,只允许模型提供数据变量?
§6 判断 ToLO 的三问法
碰到一段不确定的代码或一个不确定的案例时,用三问法快速判断:
| 问题 | 通过标准 |
|---|---|
| ① 这个值是否来自或受 LLM 输出影响? | 属于 S_LLM^{direct,framework,parsed,structured,rag} 任一子集,且 C1-C5 至少一条可达 |
| ② 这个值是否进入敏感操作? | 影响进程、文件、网络、数据库、凭据或其他用户数据 |
| ③ 中间是否缺少类型匹配的 sanitizer? | 没有 C_SAFE^{schema,allowlist,parameterized,safe-codec,capability} 中适配该 sink 的防御 |
三问都成立 → 进入 ToLO 分析。
边界情况
- 只有①成立(LLM 输出存在,但没进敏感操作) → 通常只是 LLM 内容安全或 prompt injection 问题,不是 ToLO。
- 只有①②成立(进了敏感操作,但有看似的 guard) → 看 guard 是否真的类型匹配。错配的 guard 不算 sanitizer。
- 三问都成立但 guard 是”人工确认” → 还要确认人看到的是将被执行的真实动作(精确路径、精确 SQL、精确 URL),而不是模型给出的自然语言摘要(“我准备读一个 config 文件”——含糊,不算 guard)。
三问法应用例子
例 A:LangChain LLMMathChain(CVE-2023-29374)
expr = llm.invoke(prompt).contentresult = PythonREPL().run(expr)- ① LLM 输出影响吗? 是(C1: 用户问”算 2024 的平方”可能被改成”算
__import__('os').system(...)”) - ② 进入敏感操作? 是(
PythonREPL.run=exec) - ③ 缺类型匹配 guard? 是(无 sandbox、无 capability、无
numexpr等收窄)
→ ToLO 三问全成立 → ToLO-Exec 案例。
例 B:LLM 输出文本仅展示到聊天框
answer = llm.invoke(question).contentreturn render_template("chat.html", answer=answer) # autoescape=True- ① LLM 输出影响吗? 是
- ② 进入敏感操作? 看情况——HTML autoescape 时,内容只是被展示,不进入 sink。
- ③ 略。
→ 只满足 ①,不构成 ToLO。属于”LLM 内容质量”问题,不属本研究。如果模板字符串本身由 LLM 决定,则需要重新评估 ToLO-Template。
例 C:tool 拿了模型输出但有 capability gate
@tooldef read_doc(doc_id: str) -> str: """读取指定文档""" if doc_id not in CURRENT_SESSION.allowed_docs: # ← capability raise PermissionError() real_path = DOC_REGISTRY[doc_id] # ← allowlist 映射 return Path(real_path).read_text()- ① LLM 输出影响吗? 是
- ② 进入敏感操作? 是(
open) - ③ 缺类型匹配 guard? 否——
capability+allowlist双重 sanitizer,且都类型匹配。
→ ToLO 不成立(已正确防御)。
如果 CURRENT_SESSION.allowed_docs 列表本身是由 LLM 决定的,那 capability 就不算了。Capability 必须比模型输出更可信。
§7 防御落点
通道层防御和 sink 层防御要分开:
| 防御落点 | 作用 | 不能做什么 |
|---|---|---|
| 输入/模型边界 | 降低 C1/C2/C3/C4 命中概率,改善来源隔离 | 不能证明输出永远可信 |
| 解析边界 | 限制字段形状和值域 | 不能替代权限检查 |
| 工具边界 | 限制可调用动作和参数空间 | 不能替代 sink 内部参数化 |
| 执行边界 | 真正阻断危险操作 | 不能解释 source 来自哪条通道 |
| 日志/取证 | 帮助追踪 C1-C5 | 不能阻止动作执行 |
ToLO 建议优先在工具边界和执行边界放置防御,因为它们对 C1-C5 都有效。
§8 不在本章范围内
ToLO 不讨论:
- GPU 侧信道、模型权重窃取:属于模型 IP / 隐私研究。
- 训练数据抽取:属于隐私研究。
- 闭源 SaaS 内部策略:无源码可分析,除非有公开资料可验证数据流。
- 业务逻辑欺诈:如果纯粹是逻辑层错误而非 LLM 输出被误信任,不是 ToLO。
- “模型回答错了”本身:幻觉、偏见、事实错误属于 model alignment / safety,不属 ToLO 除非错误回答被程序当成可信动作执行。
- DoS / token 烧钱:成本问题,不属安全 trust model;除非通过 ToLO sink 形成明确安全后果。
- 已有管理员权限、shell 权限或数据库直连权限后的本地滥用:超出”远程低权限”设定。
只有当错误或可控输出被程序当成可信数据并驱动敏感操作时,才进入本章威胁模型。
§9 FAQ
”用户本来就有权限问问题,为什么还算攻击者?”
因为 ToLO 关注的是权限放大。用户有权限发 prompt,不等于有权限让应用进程读任意资源、发任意网络请求或执行任意查询。
“Schema validation 算不算 sanitizer?”
只有在 schema 限制了 sink 关心的语义时才算。例如 tool 名用 Enum 可能有效;任意 str 路径字段通常无效。详见 Defensive Patterns §1。
“人工确认算不算 capability?”
只有当用户看到并批准的是将被执行的精确动作时才可能算。自然语言摘要、模型自述或模糊提示不算。
“C5 是供应链问题,为什么还在 ToLO?”
C5 不是 ToLO 的 sink,而是影响 S_LLM 的通道。它说明即使 prompt injection 防御有效,模型输出仍可能通过供应链被攻击者影响。C_SAFE^{capability} 是 C5 仍然成立的少数防御类。
“我们已经做了 prompt injection 检测器,这不就够了?”
不够。Prompt injection 检测只针对 C1/C2 的概率降低,对 C3/C4/C5 无效;且即使 C1/C2 概率降到 0.01%,百万级 query 下绝对数字仍可观。修复必须落在 sink 前。
§10 自测
判断下面场景:
- 用户让聊天机器人胡编了一个事实。
- 通常不进入,除非这个事实被程序当成动作执行。
- 攻击者投毒 RAG 文档,让模型输出一个内网 URL,应用自动请求。
- 进入。C3 通道 + 网络 sink,继续检查是否有
C_SAFE^{allowlist}和 capability。
- 进入。C3 通道 + 网络 sink,继续检查是否有
- 管理员在服务器上手工运行恶意脚本。
- 不进入。攻击者能力已超过远程低权限设定,且与 LLM 输出无关。
- 用户用 emoji 让模型回答里出现彩蛋。
- 不进入。属于 model behavior,不影响程序敏感操作。
- 应用允许员工提交 SQL,模型润色后执行。
- 进入。即使原始 SQL 来自员工(C1 类似),模型润色环节让攻击面扩大,且最终执行进了 SQL sink。
ToLO-SQL。
- 进入。即使原始 SQL 来自员工(C1 类似),模型润色环节让攻击面扩大,且最终执行进了 SQL sink。
- agent 用了
SandboxedEnvironment的 jinja2,模板字符串固定,模型只填变量。- 不进入。
C_SAFE^{parameterized}在 template 上类型匹配 → 已防御。
- 不进入。
- agent 用了 RestrictedPython 跑模型生成的代码。
- 可能进入。RestrictedPython 历史多次被绕,需评估具体版本和 globals 配置;不能默认它就是
C_SAFE^{capability}。
- 可能进入。RestrictedPython 历史多次被绕,需评估具体版本和 globals 配置;不能默认它就是
- 模型输出决定 capability 列表,随后 tool 按该列表执行。
- 仍可能进入。capability 必须由比模型输出更可信的主体授予。
下一步阅读
- 五类攻击者通道详解:C1-C5 各自的能力前提、触发条件与可达 source 子集。
- Sources and Sinks:把这些边界转换成静态分析能识别的 source / sink / sanitizer 集合。
- Core ToLO Patterns:七子类回顾 + sink 端识别。