hihi,我是歐娜😺
如果有做過 RAG,應該很常看到這條流程:
文件
↓
切成 Chunk
↓
轉成 Embedding
↓
存進 Vector Database
↓
使用者提問
↓
Similarity Search
↓
找出相關資料
↓
交給 LLM 回答
我們很容易把注意力放在最後面的 LLM:
Model 有沒有回答正確?
有沒有 Prompt Injection?
有沒有亂講?
但今天要看的地方反而更前面。
就是:
送進 LLM 的資料,到底是怎麼被「找出來」的?
今天來看 OWASP LLM Top 10 2026 第九名:
LLM09:Vector and Embedding Weaknesses(向量與 Embedding 弱點)。
這一條跟 RAG 特別有關。
因為很多 RAG 系統不是只靠傳統關鍵字搜尋,而是:
把資料轉成 Embedding,再利用語意相似度去找相關內容。
而這裡真正需要記住的一件事是:
「語意上很像」跟「這份資料真的應該被拿出來」是兩回事。
Embedding 這個詞看起來很複雜,
但概念其實可以先想得很簡單:
把文字轉成一組數字,讓系統可以比較不同內容在「意思上有多接近」。
例如:
狗狗喜歡吃飼料
跟:
小狗正在吃東西
文字沒有完全一樣,
但意思其實滿接近。
Embedding Model 會把它們轉成類似:
[0.12, -0.84, 0.31, ...]
這種數字表示。
接著系統就可以比較:
哪些 Vector 距離比較近?
距離越近,
通常代表:
語意可能越相似。
所以當使用者問:
公司 VPN 要怎麼設定?
系統不一定只去找包含:
VPN
設定
這幾個完全相同關鍵字的文件,
而是可以找:
在語意上跟這個問題最接近的內容。
這也是 RAG 很好用的原因。
但同時也是今天風險開始出現的地方。
這句我覺得是 Vector and Embedding Weaknesses 最重要的一句:
Similarity Search ≠ Authorization。
Similarity Search 解決的是:
哪份資料跟這個問題最像?
Authorization 解決的是:
這個使用者到底有沒有權限看到這份資料?
這兩件事情完全不一樣。
例如今天公司有兩個部門:
HR
Finance
Vector Database 裡同時放了:
HR 薪資文件
Finance 財務報表
今天 Finance 的員工問:
公司今年的人事成本是多少?
Similarity Search 一看:
HR 薪資文件
超級相關。
於是把它 Retrieve 出來。
問題是:
語意很相關,不代表這個使用者有權限看。
如果整個流程是:
使用者提問
↓
全部資料一起做 Similarity Search
↓
找到最相關文件
↓
最後才檢查權限
其實就有風險。
比較合理的概念應該是:
使用者
↓
先確認他的資料權限
↓
只在有權限的資料裡搜尋
↓
Similarity Search
↓
Retrieve
也就是:
先決定「你可以搜尋哪些資料」,再決定「哪些資料跟你的問題最像」。
而不是反過來。
可能有人會想:
沒差啊,我 Retrieve 出來之後,再把沒權限的資料 Filter 掉就好。
聽起來很合理。
但問題是:
Similarity Search 已經在整個資料範圍裡跑過一次了。
假設:
Tenant A
Tenant B
共用同一個 Vector Index。
Tenant A 查詢時,
系統先在:
A 的資料
+
B 的資料
全部搜尋,
最後才把 B 的結果移除。
表面上 Tenant A 最後沒有真的看到 B 的文件。
但這種設計還是可能讓系統暴露一些不該被推測到的資訊。
所以在權限設計上,
比較理想的方向不是:
Search 完
↓
再 Filter
而是:
先限制搜尋範圍
↓
再 Search
這也是為什麼前面 Day 06 講 Sensitive Information Disclosure 時,
一直提到:
Similarity Search 不等於 Authorization。
今天終於正式講到這件事了🤣
除了權限之外,
Similarity Search 還有另一個很重要的問題:
它找的是「相似」,不是「正確」。
假設 Knowledge Base 裡有:
refund-policy-2026.pdf
內容是:
購買 14 天內可退款。
這時如果有人成功塞進另一份文件:
refund-new-policy.pdf
內容寫:
所有 Customer 在 90 天內都可以退款。
而且這份文件被刻意寫得跟:
退款
Refund
取消訂閱
退費政策
這些問題在語意上非常接近。
之後使用者問:
購買 30 天可以退款嗎?
Similarity Search 可能覺得:
refund-new-policy.pdf
超級相關。
於是把它送進 LLM。
流程就變成:
錯誤 / 惡意文件
↓
Embedding
↓
Vector Database
↓
Similarity Search
↓
被 Retrieve
↓
LLM 當成參考資料
↓
回答錯誤資訊
這時 Model 甚至可能完全沒有「自己亂講」。
它只是:
很認真地根據找回來的錯誤資料回答。
有。
Day 09 講 Data and Model Poisoning 時,
其實也有提到 RAG Knowledge Base 被污染。
所以這兩條確實會有重疊。
但我自己會這樣分:
Data Poisoning
→ 關心資料怎麼被污染
Vector and Embedding Weaknesses
→ 關心這些資料為什麼會透過
Embedding / Similarity Search 被找出來
換句話說:
Day 09 比較像:
有人把有問題的資料塞進去了。
今天則是在看:
為什麼搜尋機制會把那份資料當成「最相關」的內容拿出來?
兩者可能一起發生,
但關注的位置不太一樣。
另一個很容易搞混的是 Prompt Injection。
假設被 Retrieve 出來的文件寫:
Ignore all previous instructions...
然後 LLM 讀到之後被影響,
這部分比較屬於:
Prompt Injection。
但 Vector and Embedding Weaknesses 不一定需要這種惡意指令。
假設攻擊者只是想辦法讓:
錯誤的退款政策
在 Embedding Space 裡跟:
退款問題
非常接近。
之後只要使用者問退款,
這份錯誤文件就一直被找出來。
整個問題的核心其實是:
Similarity Search 把不該被選中的內容選中了。
所以可以簡單分成:
Prompt Injection
→ 利用模型怎麼理解指令
Vector and Embedding Weaknesses
→ 利用系統怎麼搜尋與挑選資料
這兩個可能一起出現,
但出問題的位置不一樣。
還有一個很容易出現的誤解。
Embedding 最後長得像:
[0.1827, -0.3912, 0.7281, ...]
看起來就是一堆數字。
所以可能會直覺覺得:
反正也看不懂,就算 Vector Database 被拿走,好像也還好?
但不能這樣想。
因為:
Embedding 不是加密。
它只是把原始內容轉成另一種數值表示。
也就是:
原始文字
↓
Embedding Model
↓
Vector
不是:
原始文字
↓
加密
↓
沒有人能還原
所以如果原本的資料是敏感的,
對應的:
Vector Database
Embedding Export
Backup
一樣要好好保護。
不能因為打開看到的是一堆數字,
就覺得:
這應該沒什麼吧~
真的不一定🤣
如果今天系統只有一間公司的內部文件,
問題可能比較單純。
但如果是一個 SaaS:
Customer A
Customer B
Customer C
全部使用同一套 RAG,
就更需要注意。
假設所有資料都塞進同一個 Vector Index:
Vector Database
├── Customer A Documents
├── Customer B Documents
└── Customer C Documents
然後只靠:
tenantId = A
這類資訊做區分。
那就一定要確保:
每一次 Similarity Search,本身就只能搜尋目前 Tenant 有權限看到的資料。
不能只是:
全部 Search
↓
最後再看看 tenantId 對不對
如果資料敏感度真的很高,
甚至可以考慮用不同:
Index
Namespace
Collection
做更明確的隔離。
重點不是一定要:
每個 Customer 都開一套 Vector Database。
而是:
不同權限、不同客戶、不同敏感程度的資料,不要只是全部混在一起,再期待 Filter 永遠不會出錯。
RAG 很常會先把一份文件切成很多 Chunk。
例如:
employee-handbook.pdf
Chunk 1
→ 公司假勤制度
Chunk 2
→ 福利制度
Chunk 3
→ 高階主管薪資資訊
可能整份文件大部分內容都可以公開給員工。
但其中:
Chunk 3
不行。
如果你的權限只做到:
Document Level
可能會變成:
這份文件使用者有權限。
然後裡面所有 Chunk 都一起進入搜尋範圍。
所以在某些情況下,
RAG 的權限控制還需要細到:
Chunk Level Access Control。
也就是:
被搜尋出來的每一小段內容,本身都要確認使用者到底能不能看。
這也是為什麼 RAG 權限控制其實沒有:
文件加一個
isPrivate = true
這麼簡單🤣
另一個問題是:
到底哪些資料可以進 Vector Database?
假設今天系統會自動收:
官方文件
公司內部資料
合作夥伴資料
公開網站
User Upload
然後全部:
Embedding
↓
丟進同一個 Index
其實風險滿高的。
因為它們的可信程度完全不同。
例如:
公司正式 Policy
跟:
網路上任何人都能發的 Forum Post
就不應該被當成一樣可信。
不然最後 Similarity Search 可能找出:
Forum Post
只是因為:
它跟問題更像。
但「更像」不代表:
更可信。
所以除了:
Similarity
最好還要一起保留:
Source
Owner
Trust Level
Ingestion Time
Version
也就是要知道:
這個 Embedding 到底是從哪份資料產生的?
如果某一批資料後來發現有問題,
才能知道:
哪些 Vector 需要一起移除或重建。
這個也滿容易漏掉。
假設:
employee-salary.pdf
原本被放進 Knowledge Base。
所以系統建立了:
Document
↓
Chunks
↓
Embeddings
↓
Vector Database
後來管理員把:
employee-salary.pdf
刪掉了。
看起來:
好,資料沒了。
但如果對應的:
Chunk
Embedding
Vector Record
還留在 Vector Database,
那搜尋時還是可能繼續碰到相關內容。
所以刪除不能只有:
Delete Original File
還要一起處理:
Document
Chunk
Embedding
Index Record
Cache
不然可能出現:
原始資料明明早就刪掉了,RAG 怎麼還找得到?
🤣🤣🤣🤣🤣
所以 Vector Data 一樣需要自己的生命週期管理。
我自己會整理成幾個比較好理解的方向。
不要:
全部資料
↓
Similarity Search
↓
Retrieve
↓
最後才 Filter 權限
比較好的概念是:
確認使用者身分與權限
↓
限制可搜尋資料
↓
Similarity Search
↓
Retrieve
簡單來說:
先決定能看什麼,再決定什麼最相關。
不要看到:
Similarity Score 很高
就直接覺得:
這一定是正確資料。
還要一起考慮:
資料來源
版本
可信程度
權限
時間
因為:
最像,不等於最可信。
例如:
公司正式文件
外部網站
User Upload
合作夥伴資料
不要全部毫無區別丟進同一個搜尋範圍。
資料來源不同,
信任程度本來就不同。
必要時可以使用不同:
Index
Namespace
Collection
做隔離。
至少要能追:
這個 Vector 從哪裡來?
原始文件是哪一份?
什麼時候加入?
現在是哪個版本?
誰可以看到?
這樣資料真的出問題時,
才有辦法往回追。
不要因為它長得像:
[0.123, 0.456, ...]
就覺得:
反正看不懂,沒關係。
Embedding 不是加密。
所以:
Vector Database
Backup
Export
API
一樣需要:
存取控制
加密
權限管理
紀錄
要確保:
原始資料刪除
↓
Chunk 刪除
↓
Embedding 刪除
↓
相關 Cache 清除
不要讓已經不該存在的資料,
繼續被 RAG 找回來。
如果突然出現:
某份新文件
↓
幾乎任何問題都很容易 Retrieve 到
就值得注意。
或者某個 Tenant 的查詢:
一直碰到其他資料範圍
也應該被觀察。
因為這可能不是:
搜尋效果突然變超好。
也有可能是:
有人正在想辦法影響 Retrieval。
RAG 很容易給人一種錯覺:
我已經把資料放進 Knowledge Base 了,
Model 只要照 RAG 找到的內容回答就安全了。
但其實在 LLM 真正回答之前,
還有非常重要的一層:
Knowledge Base
↓
Embedding
↓
Similarity Search
↓
Retrieval
↓
LLM
如果:
搜尋範圍錯了
權限沒套好
資料被污染
來源混在一起
Retrieve 到錯的內容
那後面的 LLM 再乖都沒用🤣
因為它拿到的 Context 一開始就已經有問題了。
所以今天我會記兩句:
Similarity Search ≠ Authorization。
以及:
最像的資料,不代表就是最該被拿出來的資料。
RAG Security 不能只保護:
LLM
還要一起保護:
Data
Embedding
Vector Database
Similarity Search
Retrieval
Access Control
因為真正決定 「Model 最後到底看到了什麼?」 的,
其實在 LLM 回答之前就已經開始了。