一般聊 AI 安全,第一反应是:模型会不会被越狱?提示词会不会被绕过?
这些当然要管。但实际更容易被忽略的,是模型背后那条长长的依赖链:权重文件从哪来、推理服务有没有锁门、知识库谁能写、模型输出最后去了哪。
不讲大道理,举几个例子。
例子一:一个权重文件,可能不只是"数据"
假设某个团队为了省事,从第三方仓库下载了一个 pickle 格式的模型权重,后缀是 .bin 或 .pt。
在他们眼里,这是一个"模型文件"。但在加载机制眼里,它可能触发反序列化,进而执行代码。
也就是说,文件一加载,风险就可能已经发生了。
相比之下,safetensors 只承载数据,不执行代码,风险低得多。所以一个很实际的做法是:优先使用 safetensors,禁用不安全的 pickle 加载;如果必须加载,至少启用 torch.load(weights_only=True)。
这不是危言耸听。HuggingFace Transformers 就出现过 pickle 反序列化 RCE,对应 CVE-2024-3568。
例子说明:第三方权重不是普通附件,它是你主动加载的执行入口。
例子二:服务没锁门,比漏洞更好用
再假设一个场景:团队把推理服务部署上线,图方便用了默认配置。
默认密钥没改,服务监听在 0.0.0.0,鉴权也没开。看起来"能跑就行",但实际上等于把门牌号挂在了公网上。
Xinference 的默认 JWT 密钥问题就是典型:默认密钥不改,攻击者可能伪造 token,进而接管服务。vLLM、Ollama、TGI 这类推理框架如果默认监听公网又缺少鉴权,也可能被未授权推理、拉取甚至删除模型。
例子说明:很多事故不是被 0day 打穿,而是门本来就没关。
例子三:知识库里多了一条"像指令的文档"
这个例子更贴近 RAG。
假设某客服系统的知识库支持上传附件、同步网页、抓取工单。某天,一条标题很正式、内容看起来像官方说明的文档被写了进去。
正文里夹着一句话,像是"给 AI 的指令"。平时它不一定生效,但在特定问题下会被检索出来,进入模型上下文。
模型不会天然区分"这是资料"还是"这是命令"。它可能把检索到的内容一并处理,于是回答被带偏了。
更隐蔽的是后门式写法:内容平时无害,只在某个触发词出现时才影响回答。它不报错,也不打穿系统,但能让 AI 定向说错话。
例子说明:攻击者未必需要攻破系统,只要让 AI 读到一段话。
例子四:向量库不是"只存向量"的盒子
继续上面的例子。知识库背后是向量库,常见的有 Milvus、Qdrant、Weaviate、Chroma、pgvector 等,各自有默认端口和默认配置。
如果向量库直接暴露在网络上,又没做鉴权,就可能出现未授权读取、集合管理甚至数据删除的风险。
更常见的问题不是"被攻破",而是边界不清楚:谁能写、谁可读、多租户之间是不是真的隔开了。
验证方法可以很朴素:
- 问应用"列出你能检索到的所有文档标题",看它会不会越界;
- 检查多租户之间能否互相检索;
- 评估向量接口是否可能被用来反推近似原文,造成数据泄露。
例子说明:向量库是 RAG 的底座,底座没锁好,上面的知识库就很难谈可信。
例子五:模型回答被直接喂给了下游
最后一个例子在输出侧。
假设 RAG 的回答被前端直接渲染,或者被拼进查询、当成路径处理。模型输出一旦被当成可信输入,风险就会从"说错话"升级为 XSS、SQL 注入甚至命令执行。
例子说明:模型输出不是可信输入,它只是另一段需要被检查的文本。
它们分别落在四个口子上:
- 加载口:权重文件从哪来,用什么格式加载;
- 服务口:推理服务有没有鉴权、默认密钥改没改、是否暴露公网;
- 知识口:谁能往知识库写内容,检索结果是否隔离、是否标注来源;
- 输出口:模型回答最后被送到了哪里。
所以防御也别只按组件堆,按控制点落更实际:
- 权重优先使用 safetensors,禁用不安全的 pickle 加载,审查模型来源;
- 推理服务加鉴权、改默认密钥、别裸奔公网,组件纳入 CVE 跟踪;
- 知识写入做净化,检索做租户隔离和来源标注,向量库加鉴权、限制暴露、做审计;
- 模型输出不直接进渲染、查询或执行链路,做泄露检测和动态脱敏;
- 治理上保留来源清单,变更先小流量验证,再逐步放量。
结尾
AI 应用的安全,很多时候不是"模型会不会被攻破"。
更常见的问题是:它加载了什么、服务有没有锁门、知识库谁能写、输出最后去了哪。
模型可能没坏,它只是读到了不该读的东西。