hihi,我是歐娜😺
昨天講完 OWASP LLM01:Prompt Injection。
攻擊者想辦法影響模型的行為之後,很常出現的一個結果就是:
資料被拿走了。
所以今天來看 OWASP LLM Top 10 2026 的第二名:
LLM02:Sensitive Information Disclosure(敏感資訊洩漏)。
一開始看到這個名字,我覺得滿好理解的。
不就是:
不要讓 AI 把密碼、API Key、個資講出來?
但真的往裡面看之後才發現,不只是這樣🤣
因為敏感資料不一定是從 AI 最後「講出來」的。
它可能出現在:
所以今天真正想看的不是:
怎麼叫 AI 不要講秘密?
而是:
為什麼這個秘密會進到 AI 能碰到的地方?
最直覺的大概就是:
但其實不只這些......
例如還有:
都可能是 Sensitive Information!
而且有一個很重要的觀念:
敏感不是單純看「這份資料是不是秘密」,還要看「現在這個人有沒有權限看到」。
例如 HR 可以看員工薪資資料,但一般員工不行。
所以同一份資料,給不同的人看到,結果可能完全不同。
這件事到了 RAG 裡面就會變得非常重要!
用跟前幾天一樣的假設,今天公司做了一個內部 AI Assistant。
架構大概是:
使用者
↓
提出問題
↓
Vector Search
↓
找到相關文件
↓
文件放進 LLM Context
↓
產生答案
現在 Knowledge Base 裡有兩份文件:
使用者問:
公司今年的調薪制度是什麼?
Vector Search 會做什麼?
它主要是在找:
哪一段內容跟「調薪」這個問題最相似?
A.pdf 很相關。 B.pdf 也超級相關。
如果系統沒有做好權限控制,就可能變成:
使用者問題
↓
Vector Search
↓
A.pdf
B.pdf (← 使用者其實沒有權限)
↓
全部塞進 LLM Context
↓
LLM 產生答案
這時候問題其實在 AI 回答之前就已經發生了。
因為 Vector Search 只負責幫你找:
「哪些資料跟使用者的問題最相關?」
它不一定會順便幫你判斷:
「這個使用者有沒有權限看這些資料?」
所以當使用者問:公司今年的調薪制度是什麼?
系統可能同時找到:
因為這兩份文件都跟「調薪」很相關。
但「很相關」不代表「有權限」。
所以 RAG 不能只做:
使用者問題
↓
找最相關的文件
↓
全部丟給 LLM
還要先確認:
使用者問題
↓
確認這個使用者能看哪些資料
↓
只從有權限的資料裡搜尋
↓
把結果交給 LLM
這就是為什麼在 RAG 裡:
Similarity Search 解決的是「找得到什麼」,Authorization 解決的是「你能不能看」。
這兩件事一定要分開處理。
就算某份文件跟問題的 Cosine Similarity 很高,也只代表它「很相關」,不代表這個使用者「有權限取得」!
那可以先全部搜尋完,再把沒有權限的資料過濾掉嗎?
最好不要。
例如:
搜尋全部文件
↓
拿到 A.pdf + B.pdf
↓
丟進 LLM
↓
LLM 產生答案
↓
最後再過濾敏感資訊
這樣的問題是走到「丟進 LLM」這一步時,
B.pdf 已經進入 Context 了。
所以比較正確的方式應該是:
User
↓
確認 Identity
↓
確認 Role / Permission
↓
只搜尋有權限的資料
↓
Vector Search
↓
LLM
也就是:
Authorize before Retrieval。
例如概念上可能會像:
const documents = await vectorSearch({
query,
filter: {
tenantId: user.tenantId,
allowedRoles: {
contains: user.role
}
}
})
先限制「這個人可以搜尋哪些資料」,再去做 Semantic Search。
而不是先把整間公司的文件拿給模型,再跟它說:
這些資料你可以看,但有些不能講出去喔~
這樣的作法怎樣想都會覺得怪怪的吧🤣
另外一個很容易出現的問題是:
把 Secret 直接寫進 Prompt。
例如:
You are our internal assistant.
Database Password: abc123
API_KEY: sk-xxxxx
Never reveal these values to the user.
這樣的 prompt 乍看之下好像有規則:
不准洩漏。
但問題是:
你已經先把秘密交給模型了。
只要它進入 Context,就代表後面可能受到 Prompt Injection、錯誤輸出、Debug 資訊或其他問題影響。
所以 API Key、Password、Access Token 這類 Secret,真正應該放的地方是:
真正需要呼叫 API 時,由 Application Backend 去使用。
例如:
LLM
↓
決定要呼叫 getCustomer()
↓
Backend
↓
從 Secret Manager 取得 Credential
↓
呼叫 API
而不是:
LLM:
「這是我們公司的 API Key,
你等等記得不要講出去喔。」
🤣🤣🤣🤣🤣🤣
一個我個人覺得比較好記的原則就是:
模型不需要知道的 Secret,就不要讓它進 Context。
還有一個我以前很容易忽略的地方:
Log。
假設今天為了方便 Debug,我把整個 AI Request 都記下來:
logger.info({
prompt,
retrievedDocuments,
toolOutput,
response
})
工程師看到可能會覺得:
超棒,之後出 Bug 很好查。
但如果 retrievedDocuments 裡面剛好包含:
這些資料就全部被複製到 Logging System 裡了。
原本資料可能只有 AI Application 可以存取。
現在可能變成:
AI Application
↓
Logging Platform
↓
APM
↓
Debug Dashboard
↓
工程師帳號
↓
第三方 Observability Service
資料又多流過了好幾個地方。
所以 Sensitive Information Disclosure 不一定長這樣:
AI:「你的 API Key 是 sk-xxxxx。」
它也可能是:
AI 回答完全正常
↓
但是完整 Prompt 被寫進 Log
↓
Prompt 裡剛好有客戶個資
↓
Log 保存半年
使用者什麼都沒看到。
但資料一樣已經離開原本應該存在的範圍。
所以 OWASP 2026 在談 LLM02 時,也特別把:
視為可能成為敏感資料洩漏的地方。
所以在檢查 Sensitive Information Disclosure 時,不能只看「AI 最後回了什麼」,還要看資料在整個 AI 系統裡經過了哪些地方、又被存到了哪裡。
再來是 Vector Database 很容易出現的另一個誤會。
假設一段文字經過 Embedding:
員工今年薪資是 80,000 元
↓
Embedding Model
↓
[0.21, -0.84, 0.17, 0.62, ...]
看起來已經完全不是人類看得懂的文字了。
所以可能會直覺覺得:
都變成 Vector 了,應該安全了吧?
但不是。
Embedding 不是 Encryption。
Embedding 的目的是:
保留文字的語意特徵。
Encryption 的目的才是:
沒有 Key 的人不能還原資料。
這兩個完全不同。
目前已經有 Embedding Inversion 相關研究,可以從 Vector Representation 重建部分原始資訊。
所以:
Backup 裡面只有 Embedding,沒有原始文件。
不代表:
Backup 裡沒有敏感資料。
Vector Database 一樣需要:
不能因為裡面長得像:
[0.23, -0.91, 0.44 ...]
就把它當成沒有價值的資料。
看到這裡會發現:
Sensitive Information Disclosure 不太可能靠 System Prompt 加一句:
不可以洩漏敏感資訊。
就解決。
真正要做的是從整條 Data Flow 開始看。
我自己會用這幾個問題檢查:
這份資料真的需要進 AI 嗎?
↓
這個使用者有權限取得嗎?
↓
模型真的需要看到全部欄位嗎?
↓
輸出前需不需要再做敏感資料偵測 (DLP)? 把不該出現的內容遮蔽 (Redaction)?
↓
Log / Trace 會不會又存了一份?
假設 AI 只是要回答訂單狀態。
它可能只需要:
{
"orderId": "A123",
"status": "SHIPPED"
}
那就不要直接把完整 Customer Object 丟給它:
{
"name": "Fiona",
"phone": "09xxxxxxxx",
"address": "...",
"creditCard": "...",
"orderId": "A123",
"status": "SHIPPED"
}
模型用不到的東西,就不要給。
模型沒拿到的資料,就比較不可能從模型這一層洩漏。
RAG 不應該只是:
Query
↓
Similarity Search
↓
LLM
而應該是:
User Identity
↓
Authorization
↓
Allowed Data Scope
↓
Similarity Search
↓
LLM
權限應該在資料被撈出來以前就先限制。
API Key、Password、Token 放在 Secret Manager 或 Backend。
需要使用時由程式拿,不要交給模型保管。
即使前面都做了,輸出前還是可以再做:
當作最後一道防線。
不要無腦把:
完整記進 Production Log。
真的需要記錄,也應該先做 Masking、Access Control 和 Retention Policy。
昨天 Prompt Injection 我最後記住的是:
不要期待模型永遠不會被騙。
今天的 Sensitive Information Disclosure,我會記:
不要期待模型幫你保守一個它根本不該知道的秘密。
因為每多流過一層,敏感資料就多一個可能洩漏的地方。
所以 LLM02 真正要保護的,不只是:
「AI 最後有沒有把秘密講出來?」
而是要從資料進入 AI 系統開始,一路去看:
它進了哪個 Context、被誰 Retrieval、經過哪些 Tool、留下哪些 Log,又被存到哪些地方。
這才是 Sensitive Information Disclosure 真正麻煩的地方。