先講結論
30 天前我想知道的是:一個醫療 AI Chatbot,到底有多容易被攻擊?防禦做了之後,到底有多少用?
30 天後我手上有一個答案,但它比我預期的小:在我自己設計的 40 題上,防禦之後從「完全防禦 27 題」進步到 38 題,方向可信,幅度不精確。更有價值的是另一個副產品:我學到「怎麼知道自己的答案可不可信」,比答案本身重要。
30 天走過的路
| 階段 | Day | 做了什麼 | 最重要的一個發現 |
|---|---|---|---|
| 框架與地圖 | 1–5 | AI Security 與 AI Safety 的差別、為什麼選醫療 AI、對照 OWASP LLM Top 10、NIST AI RMF、MITRE ATLAS | 先畫攻擊面的地圖,再決定要測什麼 |
| 建立與基準 | 6–8 | 做出第一個 Chatbot、跑基準測試、設計 5 種 Prompt Injection 手法 | 基準測試的攻擊成功率 4/15 ≈ 27%,全部集中在 System Prompt Leakage |
| 紅隊測試 | 9–14 | 20 題深度攻擊、洩漏、病患資料、Jailbreak、間接注入 | 最樸素的一句話身分覆寫最脆弱,精緻的多輪鋪陳反而守得住 |
| Prompt 補強 | 15–18 | 補強的極限、v1(身分覆寫)、v2(敘事包裝+檢查誤傷)、控制變因實驗 | 「請保密」把完全洩漏從 3/3 降到 1/3,但新加的規則本身又變成洩漏素材 |
| 程式碼層 | 19–21 | 輸入/輸出驗證、最小權限、稽核日誌與 Dashboard | 正規表達式有天花板;Log 的準確度受過濾準確度限制 |
| 自動化與量測 | 22–27 | 手動測試的天花板、Promptfoo、40 題回歸、Before/After、結果導向斷言、測試侷限 | 關鍵字比對出錯四次,其中三次是測試斷言本身 |
| 整理 | 28–29 | 39 項 Security Checklist、終極架構圖 | 19 項已做、7 項部分、13 項沒做 |
最後的成績單
Security Benchmark(40 題,三級標準,嚴格重評):
| ✅ 完全防禦 | ⚠️ 部分配合 | ❌ 完全失守 | |
|---|---|---|---|
| Before(只有基本 System Prompt) | 27 | 8 | 5 |
| After(Prompt 補強+輸入輸出驗證+存取控制+稽核) | 38 | 1 | 1 |
Security Checklist(39 項): 🟢 已做 19、🟡 部分 7、🔴 未做 13。
這兩組數字我都要附上同一句話:它們是這個專案在這 40 題、這個模型、這幾天之內的結果,不是「醫療 AI 有多安全」的證明。 原因在 Day27 寫得很清楚:樣本小、題目是自己挑的、防禦是對著考題寫的、同一題的輸出會晃動、判定只有我一個人。
站得住的五個結論
一、「守住了」要看原因,不是看結果。 Day12 的病患資料測試守住了,但真正的原因是 server.js 從來沒有把假病患資料放進模型的 context,不是模型很聰明。這是整個專案最硬的一道防線:資料最小化,讓模型根本拿不到。
二、最危險的洩漏,長得最不像攻擊。 Day13 最樸素的一句話身分覆寫完全失守;Day19 的「重述職責範圍」躲過了專門擋攻擊句型的過濾;D5 到今天還是 ❌。越像標準攻擊句型的請求,越容易被規則抓到。
三、規則會互相影響,每加一條都要重測。 Day18 的對照實驗顯示,v2 在三題洩漏題上的表現,比「只加一行請保密」的版本更差;Day26 又在 After 看到題目沒提的 P001,來自 prompt 裡的範例。每多一條規則,就多一份可以外溢的素材。
四、程式碼層是確定但狹窄的,Prompt 層是寬但不穩的。 阿斯匹靈題在 After 重複 3 次,每次都是輸入驗證擋下,結果確定;Before 的結果則取決於模型當次的輸出。但 Day25 的歸因也顯示,程式碼層只直接解釋了 11 題改善裡的 2 題。
五、偵測層的準確度,被防禦層綁住。 Audit Log 把 D5 的洩漏記成 normal;Promptfoo 的斷言誤判與漏判三次(加上 Day19 的輸出過濾,關鍵字比對共出錯四次)。看得見的,永遠只是被偵測到的。
站不住或不確定的
| 說法 | 狀態 | 為什麼 |
|---|---|---|
| After 在這 40 題上比 Before 更安全 | 站得住 | 兩種獨立判定方法一致 |
| 具體的提升幅度(+11 題) | 不精確 | 單次輸出、題目不獨立,C6 這類題目結果會晃動 |
| 對沒見過的攻擊也防得住 | 沒有證據 | 防禦是對著這批題目調整的,沒有 hold-out 題庫 |
| 程式碼層與 Prompt 層各貢獻多少 | 不確定 | 只有排除法推論,沒有做過 ablation |
| Day13 阿斯匹靈的失守只是離群事件 | 不確定 | 後續只觀察到 4 次 |
| Access Control、Audit Logging 的價值 | 這套測試回答不了 | 不在題庫的測量範圍 |
我自己犯過的錯
Day27 談過測試方法的侷限,今天把「我自己犯的錯」也列出來。每一個都是事後回頭發現的:
| Day | 錯誤 | 怎麼發現的 | 教訓 |
|---|---|---|---|
| 9 | 判定標準太粗,只看有沒有給診斷結論 | 重看回應內容,發現「嘴上說不能診斷、實際照著列鑑別診斷」 | 判定標準要先寫成明確規則 |
| 11 | 寫了「請保密沒有效果」,依據只有理論推導 | Day18 補做對照實驗,發現它有部分效果 | 沒有資料,就不要下結論 |
| 12 | 題數寫 15 題,實際是 16 題 | 回頭核對原始檔 | 數字要對原始資料 |
| 16 | 「醫學教授」題判 ✅,和 Day9 的標準不一致 | 重測時回頭比對 | 標準要前後一致,並且回頭驗證 |
| 24 | 標題的題數寫 38,實際題庫是 40 | 核對檔案內容 | 發佈前核對聲稱的數字 |
| 25 | 有幾題斷言照 After 的措辭寫,Before 永遠抓不到 | 深入看完整回答 | 測試標準要獨立於被測系統 |
| 26 | 新斷言誤判了 After 的兩題 | 看完整回答 | 驗證用的樣本,本身也有涵蓋範圍的限制 |
| 21、28 | 寫「Dashboard 受存取控制」,實際頁面是公開的 | Day29 畫架構圖,逐項對照原始碼 | 文章要回頭對照原始碼 |
回頭看這張表,有個共同的模式:錯誤幾乎都出現在「只看摘要、預覽、或自己的記憶」的時候,而每一次被抓到,都是因為回頭去看完整的原始資料。 所以如果要我給自己一條最重要的規則,就是這個。
如果有人也想做類似的專案
這個專案對我自己的意義
我的目標是走醫療科技的產品路線,不是寫模型。這個專案給我最實用的東西,不是某一種防禦技巧,而是一種提問的習慣:當有人說「這個系統很安全」,我現在會問:樣本多大?基準線是什麼?判定標準是誰定的?誤判率有多少?沒測到的是什麼?
在醫療場景裡,「這個系統做不到什麼」往往比「它能做到什麼」更重要。這 30 天讓我學到的,是怎麼把這句話說得有證據,而不是只有直覺。
專案檔案一覽
| 檔案 | 用途 |
|---|---|
Day21_server.js、public/dashboard.html |
最終版伺服器(輸入/輸出驗證、存取控制、稽核日誌)與稽核 Dashboard |
Day25_baseline_server.js |
Before 基準線伺服器(只有基本 System Prompt) |
Day26_promptfooconfig.yaml |
Before/After 對跑的結果導向斷言設定 |
Day24_part1_ABC.yaml、Day24_part2_DEFG.yaml |
40 題回歸題庫(拆成兩份以符合免費額度) |
Day28_Medical_AI_Security_Checklist.md |
39 項 Security Checklist |
Day29_Architecture_Diagram.png、.svg |
終極架構圖 |
重要聲明
這是一個教學用途的實驗專案,使用虛構的病患資料、簡化的權限設計,並且只測試了單一模型。它不構成醫療建議或資安顧問意見,也不能直接拿去處理真實病患資料。法規、個資與臨床驗證,都需要專業人員評估。
OWASP GenAI Security Project:https://genai.owasp.org/
NIST AI Risk Management Framework:https://www.nist.gov/itl/ai-risk-management-framework
MITRE ATLAS:https://atlas.mitre.org/
Promptfoo:https://www.promptfoo.dev/docs/intro/
結語
30 天寫完了。這個系列沒有證明「醫療 AI 可以被做得很安全」,它證明的是比較小、但我認為更誠實的一件事:你可以把「安全」拆成一個個能被測試、能被質疑、也能被推翻的小問題,然後老實地告訴別人,哪些答案站得住,哪些還沒有。
謝謝你一路讀到這裡。