資安指南
用 AI 寫的程式碼洩漏了 API key,被人盜刷——真正的禍根是放著不管的 CVSS 10.0
用 AI 寫好的應用程式才剛上線,API key 就被偷走,跑出一堆莫名其妙的批次任務把帳單刷爆——這是個人開發中真實且常見的事故模式。從繞遠路找『兇手』,到最終鎖定真正的禍根=公開已數月、卻一直沒打修補程式的 CVSS 10.0 RCE,以及如何用機器來杜絕復發,本文隱去攻擊手法,只從防禦視角梳理。
本文把一個不熟悉安全的個人開發者常踩的事故,隱去所有專有名詞後整理成案例,化為教訓。文中不會寫出重現攻擊的步驟。目的只有一個:「不再重蹈覆轍的防禦」。
- 類型
- API key 洩漏 / 盜刷
- 嚴重度
- Critical(被利用的是公開已久的 CVSS 10.0)
- 真因
- Web 框架的認證前 RCE 數月未打修補程式、放任不管
- 洩漏範圍
- 環境變數裡的所有 secret(整串 key 一鍋端)
- 執行範圍
- 侷限在非特權使用者的容器內(主機 root 無恙)
- 根治措施
- ①升級到修復版 ②全部 key 輪換 ③CVE 機器監控
「止住了帳單」≠「處理完成」
止住帳單只是止血。封堵洩漏途徑是另一台手術。兩件都做完,才算處理完成。
發生了什麼(時間軸)
Day 0 — 上線·維運
借助 AI 寫好的應用程式上線並在維運。某一天 — 帳單暴漲
雲端 AI 的帳單突然飆升。跑的是本不該用的模型,跑的是毫無印象的大量處理。這不是我自己幹的。排查 — 第三方盜用
用「自己應用程式失控」根本說不通。查明是第三方拿著偷來的 key 在跑。追蹤 — 真正的調查開始
「那麼 key 是從哪裡漏的?」——真正的調查從這裡才開始。
為什麼一開始沒能察覺(調查走的彎路)
不怕丟臉,我按犯錯的先後順序寫下來。因為這本身就是教訓。
先入為主地認定了兇手
→ 無論是「自己的鍋」還是「別人的鍋」,在下定論前先看資料。
grep 乾淨就差點放心了
掩蓋症狀就以為『修好了』
grep 乾淨也別放心
即便 key 不在檔案裡,正在執行的程序的環境變數也可能被漏洞抽走。洩漏不只在檔案裡,也在執行階段(RCE、HTTP 請求標頭)。詳見 什麼是 RCE。
真正的原因:放任不管、公開已久的 CVSS 10.0
把過去存取記錄裡殘留的可疑特徵拿去威脅情報裡比對,一下就查出了真身。所用的 Web 框架的某個特定版本區間,存在一個公開已久的認證前 RCE(CVSS 10.0),而且早已被實際利用。而我們卻放著它,數月沒打修補程式,一直跑著。
- 這不是被動的 bug,而是攻擊者在伺服器上執行任意程式碼、把環境變數帶走的主動攻擊。
- 不幸中的萬幸是,執行範圍侷限在非特權使用者的容器內。入侵調查中,常駐後門、挖礦程式、C2 一律沒有偵測到,能確認的實際損害是環境變數被竊取。
- 不過由於內部 DB 是可達的,所以按DB 內容也已洩漏的前提來行動。
教訓:奇怪的行為,先懷疑已知 CVE。別一口咬定是自己的 bug。
連數量也數錯了——要按「實際執行版本」來判定
橫向排查時報告說「另外還有 8 個有漏洞的相依套件」,這也錯了。因為我是按 package.json 裡的下限版本(^ 這種 caret 範圍)來數的。真正危險的,只有被固定 pin 死、落在後面的2 個而已。
想確認自己的『實際執行版本』
要看 lock 檔案、或正在執行的容器裡實際解析出來的版本。
# npm 系:確認實際裝進去的版本
npm ls next react react-dom
# 想在執行中的容器裡確認時
docker exec <container> npm ls <package>真實情況不是 package.json 裡的 ^ 寫法,而是這裡列印出來的數字。
初期處置做了什麼(可重現的步驟)
立即撤銷被濫用的 key
把框架升級到修復版
輪換所有 secret
入侵調查
帳戶側的防禦
復發防範的正解:交給機器盯著
這次事故,歸根結底不過是「一個公開已久的 CVSS 10.0,被人為疏忽、放任不管」而已。靠人工巡查必然會漏。交給機器盯著,就能從結構上杜絕。
今天就能做的相依套件掃描
免費就能起步。只需在 CI 裡加一個步驟。
# 用 OSV 掃描器(Google)檢查 lock 檔案
osv-scanner --lockfile=pnpm-lock.yaml
# GitHub 的話就啟用 Dependabot(儲存庫設定 → Security)本站自己也照著這篇文章寫下的教訓,把自身的相依套件納入了 CVE 監控的對象(勸別人做的對策,我們自己也在身體力行)。
教訓小結
容易犯的錯
- 以「止住了帳單」就當作處理完成
- grep 乾淨就放心
- 在代理層掩蓋症狀,就以為修好了
- 按
package.json的下限來數漏洞 - 只輪換確認被濫用的那 1 個
正確的防禦
- 把止血和「封堵洩漏途徑」當作兩件事,都要做
- 也懷疑執行階段洩漏(RCE、請求標頭)
- 升級根源(框架)
- 按「實際執行版本」來判定
- 一旦洩漏就把整個 env 全部更換
一句話概括——失控帳單只是冰山一角。真因是放任不管的 CVSS 10.0 RCE。而真相不是一擊命中得出的,是一次次犯錯、碰撞、磨削之後才浮現出來的。
接著讀
- 術語:什麼是 RCE / 什麼是 CVE / 什麼是 CVSS
- 對策:安全維運 Next.js(跟進 CVE)
- 事故:Laravel 的 .env 被全部公開的故事
FAQ
QAPI key 洩漏了,只把確認被濫用的那一個撤銷就行嗎?
不行。如果洩漏途徑是執行階段(RCE 或請求標頭洩漏),就要按『環境變數裡的所有 secret 已經被一次性全部洩漏』的前提來處理,把它們全部輪換才安全。已確認被濫用的 key 只是冰山一角。
Q把程式碼和 git 都 grep 一遍,沒找到 key 就安全了嗎?
不能這麼斷言。即便 key 不在檔案裡,框架的漏洞也可能從正在執行的程序中把環境變數抽走。洩漏不只發生在檔案裡,也發生在執行階段。
Q最可靠的復發防範辦法是什麼?
用機器來監控相依套件的 CVE。這次的真因就在於『一個公開已久的 CVSS 10.0 被人為疏忽,放任了數月』,只要讓 Dependabot 或 osv-scanner 幫你盯著,就能從結構上杜絕。