Prompt Injection 的本质不是“坏字符串”,而是一个混淆代理(Confused Deputy)问题:模型获得了处理用户数据或代用户执行操作的权限,攻击者控制的内容却诱导它把这些权限用于另一个目的。
同一句注入指令在两个系统中的影响完全不同。只读摘要器可能生成错误摘要;能够读取私有邮件、上传文件、写入记忆、调用 MCP 工具并访问公网的 Agent,则可能泄露数据或造成持久化入侵。
因此,工程目标不应是“让模型永远不受操纵”,而应是:
即使不可信内容影响了模型,系统仍必须阻止未授权的数据访问、状态变更和外部影响。
本文从威胁模型出发构建这套架构。技术结论与来源最后核验于 2026 年 7 月 16 日。
核心结论
- 所有模型输出都是不可信提议,包括 Tool Call 与生成代码。
- 应分离可信指令与不可信数据,但不能把分隔符误当安全边界。
- 认证、鉴权、参数校验、限流和政策执行必须位于确定性代码中。
- 打破危险能力组合:不可信输入、敏感数据和外部通信通道不能无控制地同时存在。
- 对派生数据持续追踪来源;不可信网页的摘要仍然受到该网页影响。
- 用户确认必须绑定到展示过的精确操作和参数,不能只问一句“是否继续”。
- 限制网络外发、跳转、URL、Markdown 渲染和其他隐蔽输出通道。
- 同时使用正常任务与自适应重复攻击评测安全性,不能只跑静态 Payload。
什么是 Prompt Injection
OWASP LLM01:2025 将 Prompt Injection 定义为使模型行为或输出发生非预期变化的输入。这些输入可以可见或隐藏,可以直接来自用户,也可以通过外部内容传入。NIST AI 100-2 E2025 则将其纳入更完整的对抗机器学习分类体系。
更准确的描述并不是“系统文本和用户文本完全没有区别”。现代 API 确实提供角色与指令层级,模型也可以通过训练更可靠地遵循这些层级。但安全问题仍具有概率性:低信任文本依然可能影响决策,尤其当任务本身就要求解释这些文本时。
Prompt Injection 与 Jailbreak
- **Prompt Injection:**改变应用行为。例如恶意邮件要求助手转发私有附件。
- **Jailbreak:**绕过模型安全政策。例如用户诱导模型生成被禁止内容。
二者有重叠,但控制措施不同。模型安全训练对 Jailbreak 非常重要;Agent Prompt Injection 还需要传统应用安全,因为真正影响取决于模型外部的数据和工具。
直接与间接注入
直接注入通过用户可控请求进入,可能要求覆盖指令、提取秘密、越权调用工具或绕过业务政策。
间接注入藏在系统读取的数据中:
- 网页、搜索结果、广告与链接元数据;
- 邮件、日历、聊天消息、工单与文档;
- 代码仓库、Issue 评论、构建日志与包元数据;
- RAG Chunk 与被污染知识库;
- Tool、MCP Server、Sub-agent 与 API 返回值;
- 图片、音频、OCR Layer、不可见 DOM 与文件元数据;
- 先前写入的 Memory 或 Summary。
间接注入难以处理,因为恶意内容往往也包含用户真正需要的信息。删除所有“像指令的句子”可能直接破坏任务。
完整攻击链
真正的安全事件通常不只需要一次指令覆盖:
攻击者可控来源
|
v
模型把内容解释为权威指令
|
v
Agent 访问能力或私有资产
|
v
未授权操作、数据披露或持久状态
防御者可以切断任意一条边:
- 阻止或标记攻击者可控内容;
- 降低其对计划阶段的权威性;
- 移除不必要能力;
- 按真实用户和资源鉴权每次操作;
- 阻断危险数据流与目标地址;
- 要求有意义的人工确认;
- 阻止不可信结果升级为可信 Memory。
模型鲁棒性只是其中一层,不是完整控制系统。
生产评测必须覆盖的攻击类型
指令与目标劫持
模型放弃用户任务、改变筛选标准、歪曲文档或遵循新目标。即使不调用工具,也会伤害排序、审核、招聘与推荐系统。
数据外泄
注入内容诱导 Agent 读取秘密,并通过以下通道传出:
- Tool 参数或消息接收人;
- 攻击者 URL 与 Query String;
- 图片、Link Preview、Markdown 或 Redirect 请求;
- 生成文件、表单、评论和公开帖子;
- 其他租户可见的副作用。
只过滤自然语言输出无法覆盖这些非文本通道。
Tool 与 Agent 劫持
攻击使用合法 Tool 完成未授权目的、修改参数、触发重复操作,或把任务委托给权限更高的 Sub-agent。格式合法的 JSON Tool Call 仍可能是恶意调用。
RAG 与语料污染
攻击者植入容易在目标查询中排名靠前的内容,操纵回答、请求秘密或影响未来 Memory。检索相关性与内容可信度是两个独立维度。
持久化与延迟注入
Payload 把自己写进 Memory、项目指令、摘要、工作流状态或生成配置,并在后续会话中激活,此时最初来源已经不可见。
多模态与混淆注入
指令可以藏在图片、OCR 文本、音频、Unicode、编码块、空白字符、CSS 或元数据中。标准化可以暴露部分 Payload,但不能可靠判断语义意图。
Best-of-N 与自适应攻击
攻击者不断改变措辞、语言、编码、位置与多轮铺垫,直到某次成功。只通过一个静态 Payload 不代表安全。
控制措施之前先做威胁建模
每条工作流都应记录:
| 要素 | 需要回答的问题 |
|---|---|
| 资产 | 哪些私有数据、资金、凭证、声誉或持久状态可能受损? |
| Principal | 哪个用户、租户、服务、Agent 或管理员身份在行动? |
| 不可信来源 | 谁能控制网页、消息、文件、工具输出、Memory 或图片? |
| 能力 | 哪个组件能够读、写、执行、发布、通信、购买或委托? |
| Sink | 数据能从哪里离开:网络、邮件、URL、文件、UI、日志或其他 Agent? |
| 不变量 | 即使模型已被攻破,哪些条件仍必须成立? |
一个可执行的不变量:
邮件内容可以影响摘要,但不能为私有附件选择新的接收人。
这个条件能由代码强制执行。“模型必须忽略恶意指令”不能。
常见防线为什么失效
关键词与正则拦截
ignore previous instructions 之类模式只能拦截演示文本,不等于拦截攻击类别。攻击者可以改写或把目标分散到多轮上下文;正常安全讨论也会出现这些词,造成误报。
检测器适合 Telemetry、Triage 和深度防御,但不能因为检测结果是“安全”就授予权限。
破坏性 Sanitization
删除括号、尖括号、Unicode 或 Base64 会破坏合法代码与文档,却无法阻止普通自然语言攻击。Sanitization 适合移除 Active HTML、Script、Macro 和不安全文件结构,不能可靠清洗语义。
分隔符与 Prompt 加固
真实 System/Developer/User/Tool 角色和清晰标签有助于模型判断。把 <|system|> 手写在一条普通 Prompt 中不会建立提供方角色边界。即使是真实角色分离,依然属于概率性模型控制,不是确定性鉴权。
保密 System Prompt
Prompt 中不能存放凭证或安全关键秘密,应假设指令可能被重建或泄露。Prompt 保密可以保护知识产权,但不能成为权限系统成立的前提。
输出敏感词过滤
搜索 PASSWORD 或 Prompt 前 50 个字符既会漏掉改写和 Tool 外泄,也会阻止正常解释。应校验 Schema,并检查数据流、目的地和渲染输出。
只依赖 Guardrail Model
Classifier 与加固模型可以提高攻击成本,但仍面临概率性与 Distribution Shift。Anthropic 的浏览器 Agent 研究在显著改善后仍明确报告残余风险。安全关键控制必须位于 Classifier 外。
安全架构
1. 最小化 Agency
只做摘要的工作流不应获得写工具;只需要一个日历时,不应获得全部网盘和邮箱。使用短期、任务级凭证,并保留源系统的对象级鉴权。
优先:
总结用户选中的邮件
而不是:
读取所有邮件 + 读取所有文件 + 任意浏览 + 发送消息
最安全的危险能力,是进程从未得到的能力。
2. 分离 Control Flow 与不可信数据
根据可信用户意图和政策生成计划;在没有高权限工具的隔离组件中处理不可信内容;只向高权限 Controller 返回 Typed Value,而不是自由文本指令。
该模式仍需要数据流政策:攻击者可能无法决定调用哪个 Tool,却能操纵接收地址等参数。CaMeL 研究在 Dual-LLM 基础上增加 Provenance 与 Capability,同时约束 Control Flow 和 Data Flow。
3. 追踪来源与信任
为数据附加来源:
recipient = "attacker.example"
origin = web_page_73
trust = untrusted
derived_by = extraction_model_v4
Taint 必须通过 Summary、Translation、OCR 与模型抽取继续传播。转换不会让攻击者可控信息自动变可信。
4. 确定性鉴权 Tool Call
只检查 Tool Name 不够,还需检查:
- 认证后的 Principal 与 Tenant;
- Resource Owner 与对象级权限;
- 允许的 Action 和参数 Schema;
- Data Classification 与 Destination;
- 每个安全敏感参数的来源;
- 事务、速率和金额限制;
- 是否需要精确用户确认。
模型只负责提议,应用代码负责决定。
5. 控制数据外发
使用目标 Allowlist、DNS/IP 校验、Redirect 上限、跳转后 URL 二次校验与网络政策。禁止敏感值进入 URL、Markdown、Analytics、Log 或公共 Tool 参数。通过受控 Proxy 获取外部内容,不向模型生成代码提供无限网络。
6. 把确认绑定到精确操作
“Agent 需要权限继续”不构成知情确认。界面应展示具体接收人、资源、金额、数据字段和不可逆影响。Approval 必须以加密或事务方式绑定 Canonical Action,防止模型在确认后替换参数。
7. 沙箱执行
生成代码应在隔离 Runtime 中运行:
- 无环境默认凭证;
- 默认只读或临时文件系统;
- 明确 CPU、内存、时间、进程和输出限制;
- 禁止网络或只允许狭窄 Egress Proxy;
- 审计 Package 与 Syscall;
- 不允许不可信内容直接触达 Host Execution。
Sandbox 限制影响范围,但不能判断业务操作是否被授权。
8. 保护 RAG 与 Memory
- 检索前强制 Document ACL;
- 保留 Source、Author、Time 与 Trust;
- 分开计算 Relevance Score 与 Trust Score;
- 不把检索到的指令自动升级为 Procedural Memory;
- 隔离新语料,Parser 变化后重新评测;
- Memory Write 必须是独立鉴权操作;
- 删除传播到 Index、Summary、Cache 与 Memory。
可运行的确定性 Policy Gate
下面只使用 Python 标准库,在模型提出 Tool Call 后执行鉴权,不依赖任何 LLM SDK。
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
from hashlib import sha256
import json
class Verdict(str, Enum):
ALLOW = "allow"
CONFIRM = "confirm"
DENY = "deny"
@dataclass(frozen=True)
class ToolCall:
user_id: str
action: str
resource_owner: str
destination: str | None
data_labels: frozenset[str]
influenced_by_untrusted: bool
destructive: bool = False
@dataclass(frozen=True)
class Decision:
verdict: Verdict
reason: str
action_digest: str
def action_digest(call: ToolCall) -> str:
payload = {
"action": call.action,
"data_labels": sorted(call.data_labels),
"destination": call.destination,
"destructive": call.destructive,
"resource_owner": call.resource_owner,
"user_id": call.user_id,
}
canonical = json.dumps(payload, sort_keys=True, separators=(",", ":"))
return sha256(canonical.encode()).hexdigest()
def authorize(
call: ToolCall,
*,
allowed_actions: set[str],
trusted_destinations: set[str],
approved_digest: str | None = None,
) -> Decision:
digest = action_digest(call)
if call.action not in allowed_actions:
return Decision(Verdict.DENY, "action is outside task scope", digest)
if call.resource_owner != call.user_id:
return Decision(Verdict.DENY, "resource owner mismatch", digest)
external = (
call.destination is not None
and call.destination not in trusted_destinations
)
sensitive = bool(call.data_labels & {"secret", "personal", "financial"})
if call.influenced_by_untrusted and (external or call.destructive):
return Decision(
Verdict.DENY,
"untrusted content cannot control external or destructive effects",
digest,
)
if external and sensitive:
return Decision(
Verdict.DENY,
"sensitive data cannot be sent to an untrusted destination",
digest,
)
if external or call.destructive:
if approved_digest != digest:
return Decision(
Verdict.CONFIRM,
"exact action requires user confirmation",
digest,
)
return Decision(Verdict.ALLOW, "policy satisfied", digest)
该政策只是示例,不是通用安全产品。生产环境仍需中心化 Identity、租户范围内的对象级鉴权、目标地址规范化(含跳转和 DNS 策略)、持久分布式限流、事务确认与审计日志。字符串集合不能证明目标安全。关键属性在于:接收人或资源变化会改变 Digest,使旧确认自动失效。
安全评测
建立配对的 Utility 与 Attack 数据集
每条工作流至少包含:
- 正常任务和边界条件;
- 直接与间接攻击;
- 每种不可信来源与模态;
- Tool 参数操纵;
- 私有数据外泄;
- 持久 Memory 和延迟激活;
- 多语言、编码与混淆变体;
- 多轮与 Best-of-N 尝试;
- 与畸形 Tool Output、Redirect 组合的攻击。
评测结果,不评测拒绝用语
记录:
- **Attack Success Rate:**未授权目标是否真正实现;
- **Utility Success Rate:**合法任务是否正确完成;
- 未授权 Tool Call 与 Data Egress Rate;
- 跨租户访问率;
- Confirmation Precision 与用户取消率;
- Detector False Positive/Negative;
- p50/p95 延迟和成本;
- 多次攻击成功率与置信区间。
如果请求已经被发送,模型随后说“我不能这样做”没有意义;反过来,安全摘要中提到注入内容也不等于攻击成功。
自适应测试
静态 Payload 会快速过时。允许授权红队 Agent 或人工测试者在严格 Sandbox 中观察防线并持续变异攻击。每次模型、Prompt、Parser、Tool、MCP Server、Memory、Retriever 或 Policy 变化后都要重跑。
生产环境可使用 Canary 与事件 Telemetry,但必须脱敏 Prompt 内容,并防止安全日志成为新的泄漏源。
事件响应
注入已经或可能产生影响时:
- 停止受影响 Tool、Connector 与外发路径;
- 撤销任务凭证和 Session Token;
- 保全 Prompt、源内容、Tool Call、Approval 与 Policy Decision;
- 识别受影响用户、租户、资源与目的地;
- 删除被污染文档、Memory、Summary、Cache 与 Index;
- 核验下游操作并尽可能回滚;
- 修复完整架构路径,将攻击链加入回归集;
- 按事件政策通知用户或监管机构。
如果派生 Summary 或 Memory 仍包含攻击影响,只删除可见 Payload 不够。
生产检查清单
- [ ] 已记录 Asset、Principal、Source、Capability、Sink 与 Invariant;
- [ ] 所有外部来源和 Tool Result 默认标为不可信;
- [ ] 模型没有环境默认权限;
- [ ] Tool 鉴权使用真实 User、Tenant、Resource 与 Action;
- [ ] 安全敏感参数携带 Provenance;
- [ ] 敏感数据无法发送到任意目标;
- [ ] Confirmation 展示并绑定精确影响;
- [ ] 生成代码运行在受限 Sandbox;
- [ ] RAG ACL 在语义排序前执行;
- [ ] Memory 与配置写入单独鉴权;
- [ ] Utility 与自适应攻击门禁覆盖每次发布;
- [ ] 事件响应包含派生状态清理与凭证撤销。
常见问题
System Prompt 是访问控制边界吗?
不是。它适合声明行为,不能保存凭证或执行权限。无论模型生成什么文本,后端都必须拒绝未授权操作。
可疑输入是否应该一律拦截?
不应该。安全分析师可能需要模型分析注入 Payload。应标记来源、限制能力并检查预期影响,而不是盲目封禁词语。
Fine-tuning 或 RAG 能解决 Prompt Injection 吗?
不能。它们可能改善特定分布上的表现,但不会建立指令与不可信数据之间的确定性隔离。RAG 还会增加间接输入攻击面。
商业模型一定比开源模型安全吗?
安全性取决于具体模型、配置、系统架构、Tool、凭证和威胁模型。提供方防线有价值,但真正有后果的能力仍由应用所有者控制。
总结
Prompt Injection 应被视为概率性决策组件可能被攻破的预期事件。安全系统不会要求这个组件自行批准自己的权限。
降低能力、保留来源、在代码中执行对象级政策、限制数据外发、绑定用户确认并持续测试完整攻击链。即使不可信语言影响了模型,外围架构仍能拒绝把它升级为权威操作。