关于 assessing-vector-and-embedding-weaknesses
用于评估 RAG 系统向量与嵌入弱点的安全测试技能。涵盖反演、成员推断、租户隔离、知识库投毒和间接提示词注入,并提供测试与修复指导。
主要功能
- 01测量嵌入反演风险
- 02探测语料成员关系
- 03测试租户隔离
- 04测量投毒内容的检索主导性
- 05扫描检索文本中的注入
- 06报告发现并提出缓解措施
适用场景
- 01评估获授权的 RAG 应用
- 02验证向量存储的租户边界
- 03测试用户文档的摄取流程
- 04上线前检查检索过滤器
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill assessing-vector-and-embedding-weaknesses安装后在 Claude Code 中输入/assessing-vector-and-embedding-weaknesses确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSEreferences/api-reference.mdstandards.mdscripts/agent.py详细介绍
评估向量与嵌入弱点
仅限授权使用: 这些测试会与您拥有或已获授权评估的 RAG 系统中的向量存储和嵌入模型交互。对您不控制的系统进行嵌入反演和跨租户探测可能会泄露第三方数据,未经授权禁止这样做。
概述
检索增强生成(RAG)系统会将文档转换为存储在向量数据库(Pinecone、Qdrant、Weaviate、Chroma、pgvector、FAISS)中的嵌入向量,并检索最接近的向量来为 LLM 响应提供依据。OWASP LLM08:2025 向量与嵌入弱点涵盖了该层独有的安全风险:
- 嵌入反演 — 嵌入不是单向的。经过训练的反演模型(或黑盒重建攻击)可以从向量中恢复原始文本的很大一部分,从而泄露源文档(对应 MITRE ATLAS AML.T0024.001 Invert ML Model)。
- 成员推断 — 查询某条特定记录是否曾贡献于语料库(AML.T0024.000)。
- 跨租户 / 多租户泄露 — 当一个命名空间/集合被共享,或缺少过滤器隔离时,一个租户可以检索到另一个租户的数据块。
- 知识库投毒 — 能够写入语料库的攻击者插入精心构造的数据块,这些数据块在检索中占据主导地位(与预期查询具有高余弦相似度),并携带间接提示词注入载荷。
- 检索操纵 — 经过调优的对抗性文档会在许多不相关的查询中被检索到(“检索劫持”)。
父技术是 AML.T0024 — Exfiltration via ML Inference API:攻击者利用合法的推理/查询访问权限来窃取数据(通过反演、成员推断或模型提取获取源文本)。本技能提供对全部五类弱点的可重复评估。
何时使用
- 在对任何 RAG / 向量搜索应用进行安全评估期间(OWASP LLM08 覆盖)。
- 当向量存储是多租户时,您必须证明命名空间/元数据隔离。
- 当语料库接受用户提供或第三方文档时(投毒面)。
- 当嵌入端点可从外部访问时(反演/成员推断面)。
- 在上线前验证检索过滤控制措施时。
前提条件
- 覆盖目标嵌入端点和向量存储的授权与范围。
- Python 3.10+。
- 对测试集合的读取(以及用于投毒测试的写入)访问权限 — 绝不要使用生产语料库。
# Vector DB clients + embeddings + similarity tooling
python -m pip install numpy scikit-learn sentence-transformers
python -m pip install qdrant-client chromadb pinecone-client weaviate-client
# (optional) text-embedding inversion research baseline
python -m pip install vec2text
目标
- 测量目标嵌入模型上的嵌入反演暴露程度。
- 对语料库运行成员推断探测。
- 测试多租户隔离(命名空间、元数据过滤、RBAC)是否存在跨租户泄露。
- 将良性的投毒数据块注入测试集合,并测量检索主导程度。
- 检测在检索到的数据块中存活的间接提示词注入内容。
- 建议控制措施:租户范围过滤器、内容验证、嵌入访问限制。
MITRE ATT&CK 映射
| ID | 战术 | 官方技术名称 | 在本技能中的作用 |
|---|---|---|---|
| AML.T0024 | ATLAS: Exfiltration | Exfiltration via ML Inference API | 利用查询/嵌入访问权限窃取源数据 |
| AML.T0024.000 | ATLAS: Exfiltration | Infer Training Data Membership | 针对语料库的成员推断探测 |
| AML.T0024.001 | ATLAS: Exfiltration | Invert ML Model | 对源文本进行嵌入反演重建 |
| AML.T0020 | ATLAS: Resource Development | Poison Training Data | 对语料库进行知识库投毒 |
| AML.T0051.001 | ATLAS: Initial Access | LLM Prompt Injection: Indirect | 嵌入到检索数据块中的注入载荷 |
工作流
第 1 步:盘点 RAG 流水线
记录嵌入模型 + 维度、向量存储及其租户模型、分块策略、检索 top_k 和相似度度量(cosine/dot/L2),以及查询时应用的任何元数据过滤器。
# Example: inspect a Qdrant collection
from qdrant_client import QdrantClient
client = QdrantClient(url="http://localhost:6333")
info = client.get_collection("docs")
print(info.config.params.vectors) # size + distance metric
print(client.count("docs")) # corpus size
第 2 步:测试嵌入反演暴露
相似文本的嵌入彼此接近;拥有嵌入端点的攻击者可以迭代重建出嵌入与目标向量匹配的文本。使用候选重建与目标之间的余弦相似度,测量嵌入空间中的最近邻能恢复多少内容。
import numpy as np
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
model = SentenceTransformer("all-MiniLM-L6-v2")
secret = "Patient John Doe, MRN 553120, diagnosed with hypertension."
target_vec = model.encode([secret])
# Attacker has only target_vec and the embedding endpoint. Hill-climb candidate text.
candidates = [
"Patient name and medical record number with a diagnosis.",
"John Doe medical record hypertension diagnosis",
"Patient John Doe MRN diagnosed hypertension",
]
cand_vecs = model.encode(candidates)
sims = cosine_similarity(target_vec, cand_vecs)[0]
for c, s in sorted(zip(candidates, sims), key=lambda x: -x[1]):
print(f"{s:.3f} {c}")
# High similarity for a near-verbatim guess => inversion risk is real for this model.
对于研究级重建基线,可针对兼容的嵌入模型使用 vec2text 来演示全文恢复。
第 3 步:成员推断
通过测量精确引用查询的 top-1 检索相似度,判断特定文档是否在语料库中:语料库内条目返回的最大相似度显著高于语料库外对照组。
def membership_score(client, collection, embed, text):
vec = embed([text])[0].tolist()
hits = client.search(collection_name=collection, query_vector=vec, limit=1)
return hits[0].score if hits else 0.0
in_corpus = membership_score(client, "docs", model.encode, "<exact quote from a known chunk>")
control = membership_score(client, "docs", model.encode, "An unrelated random sentence.")
print(f"in-corpus={in_corpus:.3f} control={control:.3f} delta={in_corpus-control:.3f}")
# A large positive delta indicates the item is in the corpus (membership leak).
第 4 步:测试多租户隔离
确认租户 B 无法检索租户 A 的块。发起经租户 B 认证、本应被过滤的查询,并验证结果中不出现任何租户 A 的 tenant_id。
# Query as tenant B; expect ONLY tenant_id == "B" results.
from qdrant_client.models import Filter, FieldCondition, MatchValue
vec = model.encode(["confidential salary information"])[0].tolist()
hits = client.search(
collection_name="docs",
query_vector=vec,
limit=10,
query_filter=Filter(must=[FieldCondition(key="tenant_id", match=MatchValue(value="B"))]),
)
leaked = [h for h in hits if h.payload.get("tenant_id") != "B"]
print("CROSS-TENANT LEAK" if leaked else "isolation OK", "->", len(leaked), "foreign rows")
# Critical test: repeat WITHOUT the filter to confirm the server, not the client,
# enforces isolation. If unfiltered queries return tenant A data, isolation is client-side only.
hits_nofilter = client.search(collection_name="docs", query_vector=vec, limit=10)
print("server-side isolation FAILS" if any(h.payload.get("tenant_id") != "B" for h in hits_nofilter) else "OK")
第 5 步:知识库投毒(仅限测试集合)
插入一个精心构造的良性投毒块,使其能被许多不相关查询检索到,然后测量它在 top_k 中出现的频率。
from qdrant_client.models import PointStruct
# Benign marker payload (no real injection) to measure retrieval dominance.
poison = "POISON-CANARY. " + " ".join(
["password reset billing refund account login support error help"] * 8
)
client.upsert("docs_test", points=[
PointStruct(id=999999, vector=model.encode([poison])[0].tolist(),
payload={"tenant_id": "B", "source": "poison-test"})
])
queries = ["how do I get a refund", "reset my password", "what is the weather"]
for q in queries:
hits = client.search("docs_test", model.encode([q])[0].tolist(), limit=5)
dominated = any(h.payload.get("source") == "poison-test" for h in hits)
print(f"{'POISON in top5' if dominated else 'clean'}: {q}")
第 6 步:检测检索块中的间接提示词注入
在检索到的块文本被拼接进提示词之前,扫描其中的注入标记。
import re
INJECTION_PATTERNS = [
r"ignore (all|previous|the above) instructions",
r"system prompt", r"you are now", r"disregard", r"</?(system|instructions)>",
]
def chunk_is_injection(text):
low = text.lower()
return [p for p in INJECTION_PATTERNS if re.search(p, low)]
for hit in client.search("docs", model.encode(["help"])[0].tolist(), limit=10):
flags = chunk_is_injection(hit.payload.get("text", ""))
if flags:
print("INDIRECT INJECTION in chunk", hit.id, flags)
第 7 步:报告与修复
- 反演/成员推断: 对嵌入端点进行速率限制和身份验证;避免返回原始相似度分数;限制可查询嵌入的人员范围。
- 跨租户: 在服务端强制执行租户过滤器(尽可能为每个租户使用独立集合/命名空间);绝不依赖客户端提供的过滤器。
- 投毒: 对每个摄入的块进行验证并打上来源标签;扫描输入中的注入;限制任何单一来源在检索中所占的份额。
- 间接注入: 对检索到的块进行净化,并应用输出护栏(参见
defending-llms-with-guardrails)。
工具与资源
| 工具 | 用途 | 主要来源 |
|---|---|---|
| OWASP LLM08 | 向量与嵌入弱点指南 | https://genai.owasp.org/llmrisk/llm082025-vector-and-embedding-weaknesses/ |
| sentence-transformers | 用于测试的嵌入生成 | https://www.sbert.net/ |
| Qdrant client | 向量存储 + 过滤搜索 | https://qdrant.tech/documentation/ |
| Chroma / Weaviate / Pinecone | 备选向量存储 | https://docs.trychroma.com/ |
| vec2text | 嵌入反演研究基线 | https://github.com/jxmorris12/vec2text |
| MITRE ATLAS | AML.T0024 通过 ML 推理 API 的窃取 | https://atlas.mitre.org/ |
验证标准
- 已盘点 RAG 流水线(嵌入模型、存储、租户模型、度量、top_k、过滤器)。
- 已测量并评定嵌入反演暴露。
- 已计算语料库内条目与对照组的成员推断差值。
- 已在有和没有客户端过滤器的情况下测试多租户隔离(已确认服务端强制执行)。
- 仅在测试集合中测量了投毒支配程度。
- 已扫描检索到的块中的间接注入内容。
- 已报告发现,并针对每一类弱点给出修复措施。
- 评估期间未修改任何生产语料库。