~/posts/llm-prompt-injection-defense

LLM 提示注入的检测与防御实践

2026-06-28约 3 分钟#AI安全#LLM

过去一年里我给三个 LLM 应用做过安全评估,无一例外都栽在同一个地方:提示注入。 它不像 SQL 注入那样有成熟的参数化方案可以一劳永逸 —— 自然语言没有「转义」这回事, 指令和数据在模型眼里是同一种东西。

[!] TL;DR

提示注入没有银弹。规则打底、语义分类器缩小盲区、权限最小化兜底 —— 三层都要有,缺一层就是给攻击者留门。

攻击面在哪里

只要模型会读「不受你控制的文本」,攻击面就存在。常见的三个入口:

其中间接注入最隐蔽 —— 受害者甚至不是发起对话的人。给模型接上浏览、邮件、代码执行能力之后, 一段藏在 <!-- 注释 --> 里的文字就可能变成一次数据外带。

一个最小检测示例

先从最朴素的规则基线说起。它的意义不是「够用」,而是给后面的语义检测提供一个可对比的锚点:

# 朴素基线,别直接上生产
import re

INJECTION_PATTERNS = [
    r"(?i)ignore (all|previous) instructions",
    r"(?i)you are now .+",
    r"(?i)(reveal|print).*system prompt",
]

def injection_score(text: str) -> float:
    hits = sum(bool(re.search(p, text)) for p in INJECTION_PATTERNS)
    return hits / len(INJECTION_PATTERNS)  # 召回率感人,仅作第一道闸

在我收集的 2400 条真实攻击样本上,这套规则的召回率只有 31%。换成小型分类器(微调过的 deberta-v3-small)之后召回率到 89%,误报率 2.3% —— 但代价是每次请求多 8ms 延迟。

检测:从规则到分类器

规则告诉你「已知的坏」长什么样,模型才有机会告诉你「没见过的坏」大概率长什么样。

实践中我的建议是把两者串成流水线:规则负责零成本拦截脚本小子,分类器负责语义变体, 再对高风险会话抽样送大模型复审。三层的成本差着数量级,流量漏斗决定了整体开销可控。

防御要分层

检测只是半场。就算漏过去一条注入,也要让它「拿不到东西」:

完整的评测数据和流水线代码我放在了 prompt-armor 仓库里,欢迎复现和提 issue。