Case Study · 醫事應用
ExClinCalc
診所臨床決策支援系統 (CDSS)
痛點
台灣全民健保覆蓋率達 99.9%、全國醫療院所逾 23,000 家,但基層診所的資訊化程度落差極大 ── 部分診所仍依賴紙本病歷、自製 Excel、或孤島式商用軟體。大型醫院端有完整 HIS / EMR 系統,但中小型診所要嘛買不起、要嘛流程不合用。
同時,演算法與 LLM 工具在醫療決策支援的應用快速進展,但兩個現實限制使其難以真正落地: 法規與責任(自動化工具不能取代醫師決策)以及資料安全(醫療資料若集中在第三方雲端不可控)。
系統架構
六種角色、共用一份 PostgreSQL;權限不寫在後端程式碼,寫在資料庫的 Row Level Security policy 內。
- 1 · 醫事人員登入
Email + 密碼(Supabase Auth)→ 進入
/pro/security完成 TOTP enroll → 之後每次登入都需 mfa-verify 才能進/pro/* - 2 · Middleware 路由保護
所有
/pro/*路由要求aal2(Authenticator Assurance Level 2 = TOTP 已驗證)。沒過就重導 enroll 頁。 - 3 · 資料庫層 RLS(核心防線)
每筆查詢由 PostgreSQL 在執行前比對 JWT 與 policy。14 張表 29 條 policy,不在後端程式裡寫權限檢查。
- 4 · Trigger 觸發稽核
登入、處方建立、SOAP 修改、藥物交互查詢等敏感操作由 Supabase trigger 自動寫入
audit_logs表(保留 90 天)。 - 5 · 邊緣節點服務
Cloudflare Workers 跑 Next.js 16 + OpenNext。Gemini API key、Supabase service role key 僅存於 Worker runtime secret,永不暴露至前端。
安全亮點 1 ── PostgreSQL Row Level Security
ExClinCalc 完整實作 14 表 29 條 RLS policy,覆蓋 SELECT / INSERT / UPDATE / DELETE 四類操作。應用層幾乎不需要寫
if (user.role === 'doctor') 這種程式碼 ── 權限規則寫在資料庫,所有查詢自動套用。
auth.uid() 連到 users 表,再依 role + clinic_id 推導出可見資料。核心優勢:
- 單一事實來源:規則在 PG 內,所有應用(web / 未來 mobile / 第三方整合)共享同一份權限邏輯
- SQL injection 也擋得住:即使攻擊者繞過應用層直接打 PG,policy 仍套用
- JOIN 自動處理:SELECT prescriptions JOIN patients,PG 對兩張表都套 policy
Trade-off
- Debug 困難:RLS 拒絕的查詢回傳「沒有 row」,無法區分「真的沒這筆」還是「policy 擋住了」。對策:dev 環境
RESET ROLE切 superuser 重跑比對 - 複雜查詢效能下降:EXISTS 子查詢在每筆 row 都跑。對策:在 anchor 欄位加 index、用 STABLE 函式快取
- Schema migration 必須同步:加新欄位要記得改 policy 否則被擋。對策:每次 migration 跑 4 角色 RLS test
→ 詳細設計思路寫在 RLS vs 應用層權限 那篇。
安全亮點 2 ── TOTP 雙重驗證
所有醫事人員角色強制 RFC 6238 TOTP,配合 Supabase Auth 的 AAL2 機制。5 次失敗鎖定 user 30 分鐘(鎖 user 不鎖 IP,攻擊者沒密碼也觸發不了鎖定)。
Trade-off
目前沒有 backup code 機制(手機掉了無法救援)。已知缺陷,未來方向:加 backup code + magic link 救援。
安全亮點 3 ── 稽核 / 秘密管理 / 供應鏈
除了 RLS 與 TOTP 兩道前線,背景另有三層常被忽略但同等關鍵的防護:
- 稽核軌跡(audit_logs)
登入、處方建立、SOAP 修改、藥物交互查詢等敏感操作由 Supabase trigger 自動寫入,保留 90 天。即使應用層被竄改,trigger 在 DB 層觸發無法繞過,可作事後鑑識。
- 邊緣秘密隔離
Gemini API key 與 Supabase service role key 僅存於 Cloudflare Worker runtime secret,永不出現在前端 bundle、git history、log 中。任何前端漏洞都拿不到後端權限。
- 供應鏈防護
GitHub repo 啟用 Secret Scanning + Push Protection(推送含 secret 直接擋下)+ Dependabot 每週掃 npm 漏洞。CI/CD pipeline 有 secret rotation checkpoint。
六種角色
SOAP 七步驟診療、ICD-10 自動建議
分診工作台、7 項生命徵象
處方調配、雙層藥物交互檢查
profiles 唯讀(限同診所)
帳號管理、藥物 DB CRUD、使用統計
同管理員 + 醫療參考值寫入
醫師工作流 ── 從 dashboard 到處方
三個畫面構成醫師端核心:候診儀表板、SOAP 七步驟病歷、處方藥物交互檢查。
Trade-off
- SOAP 七步是我為「強制完整性」自己拆的,不是 SOAP 標準。醫師可能覺得繁瑣 ── 未來方向:可選跳過 + LLM 自動補全
- 12 組藥物交互不是窮盡所有組合,只覆蓋「最常見、最致命」。未來方向:接 FDA OpenFDA API + LLM 補規則庫覆蓋率
多角色協作示範
RBAC 不只在後端。同一份 schema 對不同角色登入會看到完全不同的工作台:
管理與分析
管理員可查看平台使用統計、處方分布、用戶活躍度。儀表板用於診所自我檢視,不對外。
技術棧
從中發現的研究問題
- 多租戶醫療系統的 RLS 設計方法論 ── 我用 29 條 RLS policy 取代應用層權限,但設計沒有系統化的方法論。怎麼從業務需求自動推導出 RLS policy 草稿?怎麼形式化驗證 policy 的完整性?
- LLM 安全嵌入 SOAP 工作流程的分級架構 ── 怎麼設計分級的 LLM 介入比例?怎麼量化評估幻覺率與覆蓋率的取捨?
- 臨床決策支援工具的真實場域評估方法 ── 學界很多 CDSS 研究停留在「功能完整度評估」,缺少「實際導入評估」。怎麼設計嚴謹的 CDSS 真實場域評估方法,包含使用者接受度、警示疲勞量測?