跳至主要內容
>_ITDITD網站資安平台

資安指南

用 AI 寫的程式碼洩漏了 API key,被人盜刷——真正的禍根是放著不管的 CVSS 10.0

用 AI 寫好的應用程式才剛上線,API key 就被偷走,跑出一堆莫名其妙的批次任務把帳單刷爆——這是個人開發中真實且常見的事故模式。從繞遠路找『兇手』,到最終鎖定真正的禍根=公開已數月、卻一直沒打修補程式的 CVSS 10.0 RCE,以及如何用機器來杜絕復發,本文隱去攻擊手法,只從防禦視角梳理。

發布於 2026-06-07 閱讀時間 2 分鐘

本文把一個不熟悉安全的個人開發者常踩的事故,隱去所有專有名詞後整理成案例,化為教訓。文中不會寫出重現攻擊的步驟。目的只有一個:「不再重蹈覆轍的防禦」。

事故概要 — INCIDENT FILE
類型
API key 洩漏 / 盜刷
嚴重度
Critical(被利用的是公開已久的 CVSS 10.0)
真因
Web 框架的認證前 RCE 數月未打修補程式、放任不管
洩漏範圍
環境變數裡的所有 secret(整串 key 一鍋端)
執行範圍
侷限在非特權使用者的容器內(主機 root 無恙)
根治措施
①升級到修復版 ②全部 key 輪換 ③CVE 機器監控
10.0
CVSS / 最高危等級
數月
放任不管的時長
整個 env
按已洩漏處理的範圍
機器監控
復發防範的正解

「止住了帳單」≠「處理完成」

止住帳單只是止血。封堵洩漏途徑是另一台手術。兩件都做完,才算處理完成。

發生了什麼(時間軸)

  1. Day 0 — 上線·維運

    借助 AI 寫好的應用程式上線並在維運。
  2. 某一天 — 帳單暴漲

    雲端 AI 的帳單突然飆升。跑的是本不該用的模型,跑的是毫無印象的大量處理。這不是我自己幹的。
  3. 排查 — 第三方盜用

    用「自己應用程式失控」根本說不通。查明是第三方拿著偷來的 key 在跑。
  4. 追蹤 — 真正的調查開始

    「那麼 key 是從哪裡漏的?」——真正的調查從這裡才開始。

為什麼一開始沒能察覺(調查走的彎路)

不怕丟臉,我按犯錯的先後順序寫下來。因為這本身就是教訓。

1

先入為主地認定了兇手

起初草率地以為「是自己程式碼裡的兜底邏輯失控了」。可只要看資料(實際跑的處理內容),那絕不可能是我自己幹的。
→ 無論是「自己的鍋」還是「別人的鍋」,在下定論前先看資料。
2

grep 乾淨就差點放心了

把全部程式碼、git 歷史、關聯儲存庫都搜了一遍 → 哪裡都沒有明文 key。我在這裡幾乎要以「無從追蹤」收手。只盯著檔案,漏看了執行階段的洩漏。
3

掩蓋症狀就以為『修好了』

在代理層把症狀掩蓋住,差點就滿足了。可掩蓋症狀,RCE 本身依然活著。不根治,洞就一直開著。

grep 乾淨也別放心

即便 key 不在檔案裡,正在執行的程序的環境變數也可能被漏洞抽走。洩漏不只在檔案裡,也在執行階段(RCE、HTTP 請求標頭)。詳見 什麼是 RCE

真正的原因:放任不管、公開已久的 CVSS 10.0

把過去存取記錄裡殘留的可疑特徵拿去威脅情報裡比對,一下就查出了真身。所用的 Web 框架的某個特定版本區間,存在一個公開已久的認證前 RCE(CVSS 10.0),而且早已被實際利用。而我們卻放著它,數月沒打修補程式,一直跑著。

  • 這不是被動的 bug,而是攻擊者在伺服器上執行任意程式碼、把環境變數帶走的主動攻擊
  • 不幸中的萬幸是,執行範圍侷限在非特權使用者的容器內。入侵調查中,常駐後門、挖礦程式、C2 一律沒有偵測到,能確認的實際損害是環境變數被竊取
  • 不過由於內部 DB 是可達的,所以按DB 內容也已洩漏的前提來行動。
看得見的症狀:毫無印象的盜刷(冰山一角)
水面:只能看到這裡
↑ 偷來的 key 有時間差地被轉賣·濫用
連同環境變數把整串 key 竊取(整個 env 洩漏)
↑ 攻擊者在執行中的伺服器上執行任意程式碼
真因:放任不管、公開已久的 CVSS 10.0(pre-auth RCE)
帳單暴漲只是冰山一角。水面下沉著『放任不管、公開已久的 CVSS 10.0』這個真因。

教訓:奇怪的行為,先懷疑已知 CVE。別一口咬定是自己的 bug。

連數量也數錯了——要按「實際執行版本」來判定

橫向排查時報告說「另外還有 8 個有漏洞的相依套件」,這也錯了。因為我是按 package.json 裡的下限版本^ 這種 caret 範圍)來數的。真正危險的,只有被固定 pin 死、落在後面的2 個而已。

想確認自己的『實際執行版本』

要看 lock 檔案、或正在執行的容器裡實際解析出來的版本。

# npm 系:確認實際裝進去的版本
npm ls next react react-dom
 
# 想在執行中的容器裡確認時
docker exec <container> npm ls <package>

真實情況不是 package.json 裡的 ^ 寫法,而是這裡列印出來的數字。

初期處置做了什麼(可重現的步驟)

1

立即撤銷被濫用的 key

止血。但這只是處理的一部分。
2

把框架升級到修復版

真正封堵 RCE。升級後,在記錄裡確認洩漏的特徵已消失。
3

輪換所有 secret

按已洩漏的前提來做。優先順序從「在任何地方都能被濫用的」開始:物件儲存 key → 工作階段簽章 key → 各類 API key → DB 憑證。
4

入侵調查

確認是否有常駐物、不正常的排程任務、對外通訊(這次都沒有)。
5

帳戶側的防禦

修改密碼並啟用 MFA,確認有沒有多出陌生的 API key。

復發防範的正解:交給機器盯著

這次事故,歸根結底不過是「一個公開已久的 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。而真相不是一擊命中得出的,是一次次犯錯、碰撞、磨削之後才浮現出來的。

接著讀

FAQ

QAPI key 洩漏了,只把確認被濫用的那一個撤銷就行嗎?
A

不行。如果洩漏途徑是執行階段(RCE 或請求標頭洩漏),就要按『環境變數裡的所有 secret 已經被一次性全部洩漏』的前提來處理,把它們全部輪換才安全。已確認被濫用的 key 只是冰山一角。

Q把程式碼和 git 都 grep 一遍,沒找到 key 就安全了嗎?
A

不能這麼斷言。即便 key 不在檔案裡,框架的漏洞也可能從正在執行的程序中把環境變數抽走。洩漏不只發生在檔案裡,也發生在執行階段。

Q最可靠的復發防範辦法是什麼?
A

用機器來監控相依套件的 CVE。這次的真因就在於『一個公開已久的 CVSS 10.0 被人為疏忽,放任了數月』,只要讓 Dependabot 或 osv-scanner 幫你盯著,就能從結構上杜絕。