Streamlit + Supabase 應用的資安稽核
這份 skill 的來歷與定位
來自一次對真實上線專案的完整稽核:11 個發現全部修掉並部署。過程中最貴的一課不是 找漏洞,而是修好之後把正式站弄壞了——「Streamlit Cloud 拿得到 XSRF cookie」 這個沒驗證過的假設被寫進了程式註解,於是「拿不到祕密就拒絕登入」的修法直接把 所有人的登入擋死。
所以這份 skill 的重點不是漏洞清單(那種東西 OWASP 寫得比誰都好),而是 怎麼把發現變成真的有效、而且不會弄壞東西的修改——以及這個技術堆疊裡哪些 「看起來合理的假設」其實是錯的。
適用:Streamlit 應用,特別是部署在 Streamlit Community Cloud、且後端用 Supabase 或直連 Postgres 的。多數判斷對任何多使用者 Python web app 都成立。
起手式:先確認有沒有威脅模型
看專案裡有沒有 SECURITY.md 之類的文件,而且內容跟程式碼對得上。
有的話,裡面的「安全不變條件」就是最好的掃描依據——直接拿每一條去對程式碼找 違反處,比漫無目的地讀程式有效得多。例如「身分只能由伺服器端決定」這條, 就直接對應到「grep 所有 DB 查詢,確認使用者識別碼都是伺服器端算出來的」。
沒有、或內容過時的話,先建立/修正它再開始掃描——拿過時的威脅模型去掃描,
會剛好略過最敏感的部分。做法見 references/threat-model.md(含可直接套用的架構)。
實際案例:那次稽核前的
SECURITY.md寫著「IP 定位送到 ip-api.com」,但程式早就 改用別的服務了,而且整個資料庫持久化層與訪客身分機制完全沒被提到。拿那份去 做 AI 資安掃描,會直接略過最敏感的資產。
流程
1. 盤點:確認威脅模型仍然成立
對照文件逐項確認,重點放在上次稽核之後的新增功能:
- 新的資料表?→ 一定要一起開 RLS 並 revoke(見
references/stack-traps.md§5) - 新的外部請求?→ 資料裡帶來的網址一律要白名單
- 新的使用者輸入欄位或身分機制?→ 那是新的信任邊界穿越點
- 新的祕密?→ 補進資產清單,並確認沒進版控(連歷史都要查)
2. 掃描
跑 references/sweeps.md 的檢查。那份按面向分組(祕密、注入、XSS、SSRF、
身分與授權、節流、日誌、依賴、資料庫權限、上傳),每組都寫了「看到什麼算有問題」
——因為多數指令的乾淨結果是沒有輸出,不知道要看什麼的話很容易誤讀。
3. 排序:嚴重度是假設,不是結論
寫下發現時先給初判嚴重度,但在驗證之前那只是假設。
實際案例:一個「資料庫沒開 RLS」的發現,初判為 High(「anon key 一外流=全站 資料可讀寫刪」)。實際查下去發現 PostgREST 需要的四個權限
anon一個都沒有, 攻擊路徑本來就是斷的——降為 Medium。先驗證再定調,不然力氣會花在錯的地方。
4. 驗證:每個發現都要能重現
這是整套流程裡最重要的一步。references/verify-recipes.md 有四種做法:
攻擊模擬、對真實資料庫的唯讀對照、把前端 JS 抽出來用 Node 實跑、在本機重現雲端條件。
兩條紀律:
- 證明迴歸測試不是空過的。 寫完測試後把舊版邏輯也跑一次,確認它真的會失敗。 一條「本來就會過」的測試等於沒有測試,而且會給人虛假的安全感。
- 跟部署環境有關的判斷,要在部署環境上量過。 本機跟 Streamlit Cloud 的差異大到
會推翻結論(
references/stack-traps.md§1)。量不到就想辦法在本機重現那個條件。
5. 修補:在最低層擋,讓不變條件無法被繞過
能在底層擋就不要在呼叫點加 if。
實際案例:簽章函式在「拿不到金鑰」時直接拋例外,而不是在呼叫端檢查一次。 這樣「用空金鑰簽章」在任何程式路徑上都不可能發生,包括以後新增的呼叫點。 空金鑰的 HMAC 攻擊者也算得出來,兩端都空時比對會通過——看起來驗過了、實際上零 綁定效果,而且完全無聲。這種失效模式只有在底層擋才能根除。
其他反覆用到的判斷:
- fail closed,但要分清楚「還沒好」與「不可能好」。 兩者混為一談的話,正常 使用者會在首輪就被誤判成攻擊。用一個明確的哨符值把兩者分開。
- 淘汰/降級的順序本身可能是安全決策。 快取或額度表滿了要丟哪一個?直覺的 LRU
常常是錯的(見
references/stack-traps.md§6)。問「攻擊者能不能利用這個行為」。 - 修完要確認功能還在。 資安修改最常見的失敗不是沒擋住,是把正常路徑也擋掉了。
6. 上線並在正式站確認
跑完測試、照專案的部署規則走,然後一定要讀正式站的日誌。
實際案例:登入故障只在雲端日誌裡看得到(一行
[AUTH]警告不斷洗版), 本機和全部測試都完全正常。沒去讀日誌的話,會以為修好了。
Streamlit Cloud 的部署細節(何時要手動 Reboot、日誌裡哪些是雜訊)見
references/stack-traps.md §10。
收尾
- 改了行為就更新威脅模型文件。 它是下次掃描的依據。
- 專案的開發筆記要寫「症狀 → 真正原因 → 量法」,不要只寫結論—— 結論沒有可驗證性,下一個人(或下一個你)沒辦法判斷它還成不成立。
- commit 訊息把驗證數字寫進去(例如「10,800 次攻擊嘗試 → 400 次通過」)。 沒有數字的資安修改,三個月後沒人知道它到底有沒有效。
參考檔案
| 檔案 | 什麼時候讀 |
|---|---|
references/threat-model.md |
專案還沒有威脅模型、或內容過時時 |
references/sweeps.md |
步驟 2,實際執行掃描時 |
references/stack-traps.md |
步驟 1/4/5。動到 OAuth、身分、資料庫連線、節流、上傳之前先讀 |
references/verify-recipes.md |
步驟 4,要證明發現為真或修法有效時 |