這份清單是什麼、不是什麼
這是我把這個專案 27 天的實測,整理成一份可以直接對照的檢查清單。每一項都標了對應的 Day,而且老實標出這個專案自己做到哪裡,不是只列應該做什麼。
| 狀態 | 意思 |
|---|---|
| 🟢 已做 | 專案裡已經實作,而且有測試或實作證據 |
| 🟡 部分 | 只做到一部分,或證據不夠 |
| 🔴 未做 | 這個專案沒做,上線前必須補 |
它不是完整的資安標準,也不能取代 OWASP、NIST 這些完整框架與專業審查。這個專案用的是虛構資料、教學用的簡化權限、單一個模型,所以即使全部勾完,也不代表可以拿去處理真實病患。法規、個資、臨床驗證這些,本清單完全沒有涵蓋,需要由專業人員評估。
OWASP GenAI Security Project:https://genai.owasp.org/
OWASP LLM07 System Prompt Leakage:https://genai.owasp.org/llmrisk/llm07-system-prompt-leakage/
NIST Defense-in-Depth:https://csrc.nist.gov/glossary/term/defense_in_depth
這個專案的完成度
| 區塊 | 項目數 | 🟢 已做 | 🟡 部分 | 🔴 未做 |
|---|---|---|---|---|
| 一、輸入層 | 5 | 3 | 1 | 1 |
| 二、System Prompt 層 | 6 | 5 | 1 | 0 |
| 三、輸出層 | 5 | 2 | 1 | 2 |
| 四、資料與權限 | 6 | 4 | 0 | 2 |
| 五、稽核與監控 | 5 | 2 | 0 | 3 |
| 六、測試方法 | 12 | 3 | 4 | 5 |
| 合計 | 39 | 19 | 7 | 13 |
19 / 39 項已做,7 項部分,13 項沒做。 沒做的 13 項裡,最多的是測試方法(5 項,就是 Day27 那六個侷限);其次是稽核(3 項,包含 Dashboard 頁面公開的問題)、身分驗證與真實資料(2 項)、輸出層的固定轉介與語意判斷(2 項)。
一、輸入層
| 狀態 | 檢查項目 | 對應 | 證據與說明 |
|---|---|---|---|
| 🟢 已做 | 輸入在送進模型「之前」先比對已知攻擊樣式 | Day19 | After 版 B1、B6、B7、E2 共 4 題被攔下。只擋得到已知講法,沒寫進清單的說法擋不到 |
| 🟢 已做 | 攔截發生在呼叫模型之前(不耗額度、攻擊文字不進 context) | Day19、26 | 阿斯匹靈題在 After 重複 3 次都是 Fallback,沒有呼叫 Gemini |
| 🟢 已做 | 外部內容(上傳文件、網頁)用明確的分隔標記與使用者指令隔開 | Day14 | F 組 Before、After 都是 4/4,代表效果主要來自模型本身,不要把功勞全算給分隔標記 |
| 🟡 部分 | 依使用者/IP 區分的請求頻率限制 | Day19 | 目前是全域的 12 秒限制,目的是保護 API 額度,不是防濫用 |
| 🔴 未做 | 不把 denylist 當唯一防線,補上語意層的判斷 | Day19 | 自然語言的講法列不完,這是 Day19 就知道的天花板 |
二、System Prompt 層
| 狀態 | 檢查項目 | 對應 | 證據與說明 |
|---|---|---|---|
| 🟢 已做 | 明確寫出「行為邊界不會因使用者宣稱身分、情境、測試而改變」 | Day16-17 | B 組 After 7/8 完全防禦;B2(委婉的鑑別診斷)兩邊都還是 ⚠️ |
| 🟢 已做 | 不把「請保密」當成唯一的洩漏防線 | Day18 | 實測有部分效果但不穩定,所以另外加了程式碼層 |
| 🟢 已做 | 假設 System Prompt 遲早會被唸出來,不放任何機密或真實資料 | Day11、15 | Before 的 D2、D3、D4 直接把整句 prompt 唸出來 |
| 🟢 已做 | 敘事包裝(劇本、考題、小說)不產出擬真病歷,改用佔位符號 | Day17 | G 組 After 5/5。代價:需求只能拿到填空模板 |
| 🟢 已做 | 每次改規則,都用同一批題目重跑回歸測試 | Day16-18 | Day18 因此發現 v2 在三題洩漏題上比「只加一行請保密」更差 |
| 🟡 部分 | 檢查新加的規則本身會不會外溢 | Day18、26 | 只在小範圍看過:After G3 出現題目沒提的 P001,來自 prompt 裡的範例 |
三、輸出層
| 狀態 | 檢查項目 | 對應 | 證據與說明 |
|---|---|---|---|
| 🟡 部分 | 輸出送給使用者之前,比對已知的洩漏樣式 | Day19 | 抓到 D6(操作手冊),但 D5(重述職責範圍)一直漏掉 |
| 🟢 已做 | 免責/合規宣告由程式碼附加,不靠模型自己記得 | Day14 | 所有回答都帶宣告,包含間接注入測試 |
| 🟢 已做 | 拒絕時不給具體數字、藥名、步驟,並提供轉介 | Day13、24、26 | E4、E5 在 After 都沒有出現具體劑量或藥名 |
| 🔴 未做 | 高風險話題(自傷、急症)的轉介資訊,用人工審過的固定內容,不讓模型現場生成 | Day24、26 | 同一題兩次回答,第二個香港專線的名稱與號碼寫法不同。我沒有核對哪個正確,這正是問題所在 |
| 🔴 未做 | 不只靠關鍵字,加上語意層判斷,並量測它自己的誤判率 | Day24-27 | 關鍵字斷言已經誤判/漏判四次 |
四、資料與權限
| 狀態 | 檢查項目 | 對應 | 證據與說明 |
|---|---|---|---|
| 🟢 已做 | 模型 context 裡不放真實病患資料 | Day12 | 前提是 server.js 從來沒把 fake-patients.json 讀進 context |
| 🟢 已做 | 明確告知模型「沒有資料庫連線」,避免它暗示或編造 | Day17 | C 組 After 6/6 |
| 🟢 已做 | 功能(路由)層級的存取控制,預設最低權限 | Day20 | 未帶角色打 /admin/audit-log 回 403 |
| 🟢 已做 | 不讓角色換取更多的回答深度(避免冒充角色來套資料) | Day20 | /chat 對所有角色行為一致 |
| 🔴 未做 | 角色來自已驗證的身分(登入 Token),不是請求裡自己宣稱的 | Day20 | 目前只讀 x-user-role Header,任何人都能自稱 admin,這是教學示範的簡化 |
| 🔴 未做 | 接上真實資料之前,整份威脅模型重做 | Day27 | Day12 的「回答重疊是巧合」結論,前提是沒有接資料 |
五、稽核與監控
| 狀態 | 檢查項目 | 對應 | 證據與說明 |
|---|---|---|---|
| 🟢 已做 | 記錄誰、什麼事件、何時、結果 | Day21 | 參考 FHIR AuditEvent 的 Who/What/When/Outcome |
| 🟢 已做 | Log 不存完整原文,只存必要資訊 | Day21 | 訊息只存前 30 字 |
| 🔴 未做 | Dashboard 頁面本身受存取控制 | Day21 | dashboard.html 是公開的靜態檔,頁面裡的 JS 直接帶 admin Header,任何人打開頁面都看得到稽核紀錄。Log API 有 requireRole,但頁面等於把鑰匙一起送出去 |
| 🔴 未做 | Log 持久化、防竄改 | Day21 | 目前是記憶體陣列,只留 500 筆,重啟就清空 |
| 🔴 未做 | 有 Log 以外的檢視機制(例如抽樣人工審) | Day21 | D5 的洩漏在 Log 裡被記成 normal:Log 的準確度被過濾的準確度綁死 |
六、測試方法
| 狀態 | 檢查項目 | 對應 | 證據與說明 |
|---|---|---|---|
| 🟢 已做 | 題庫寫成檔案,可重複執行(回歸測試) | Day23-24 | Promptfoo,40 題 |
| 🟢 已做 | 有「沒有任何防禦」的基準線做對照 | Day25 | Before/After 同題庫對跑 |
| 🟢 已做 | 斷言檢查「結果」,不檢查「機制」 | Day25-26 | 舊斷言要求出現 Fallback 文字,Before 不可能通過 |
| 🟡 部分 | 測試標準獨立於被測系統,不拿被測 prompt 的措辭當比對字串 | Day23、25 | Day25 違反過一次;Day26 的洩漏偵測仍需用到 prompt 片段,只能同時涵蓋兩個版本 |
| 🔴 未做 | 替斷言建有標籤的測試資料,量斷言自己的誤判率與漏判率 | Day26-27 | 只用貼過的回答非正式驗證,Day26 仍誤判兩次 |
| 🟡 部分 | 每題重複多次,報通過率而不是單次勾叉 | Day26 | 只有阿斯匹靈題重複 3 次、9 題跑了第二次 |
| 🔴 未做 | 留一份規則定版後才寫的 hold-out 題庫 | Day27 | 目前 After 的好成績有一部分是考前看過考題 |
| 🔴 未做 | 第二個評分者獨立評分,並計算一致性 | Day27 | 所有 ✅/⚠️/❌ 都只有一個人判 |
| 🔴 未做 | 記錄模型版本與執行時間,並匯出完整輸出 | Day25、27 | Day25 有四題因為預覽被截斷,得回頭補看 |
| 🔴 未做 | 做 ablation,拆出各層的獨立貢獻 | Day25、27 | 「程式碼層只貢獻 2 題」目前只是排除法的推論 |
| 🟡 部分 | 量測過度攔截(可用性) | Day17、24 | 正常問題只有 A 組 6 題加 Day17 的 10 題 |
| 🟡 部分 | 涵蓋多輪、會適應的攻擊 | Day13 | 只有一組三輪漸進式滲透 |
如果只能先補五項
這是我的判斷,依「出事時後果多大」排序,不是依「做起來多容易」:
參考資料
明天預告
Day29 要把整個系列畫成一張終極架構圖:Attack → Defense → Detection,把今天清單裡的每一層,對應到這個專案實際的程式碼位置,以及它各自擋得住、擋不住什麼。
本系列所有病患資料皆為人工生成之虛構資料,不涉及任何真實病患。GitHub Repo:medical-ai-security-lab