本文把沒有資安背景的個人開發者常遇到的事故,隱去所有可識別的細節後整理成教訓。文中沒有重現攻擊的步驟。目的是防禦,讓同樣的錯誤不會發生在你身上。
- 類型
- API key 洩漏 / 盜刷
- 嚴重度
- Critical(被利用的是公開的 CVSS 10.0)
- 根本原因
- Web 框架的認證前 RCE 數月未修補
- 洩漏範圍
- 環境中的所有 secret
- 執行範圍
- 侷限在非特權容器內(主機 root 未受害)
- 根治措施
- ①升級到修補版 ②輪換所有 key ③以機器監控 CVE
「止住了帳單」≠「處理完成」
止住帳單只是暫時措施。封堵洩漏途徑是另一項工作。兩件都做完,才算處理完成。
發生了什麼(時間軸)
Day 0 — 上線並運作
借助 AI 寫好的應用程式上線,在正式環境中運作。某一天 — 帳單暴漲
雲端 AI 的帳單突然飆升。跑的是絕不會用到的模型,做的是不屬於自己的大量處理。調查 — 第三方濫用
「自己的應用程式失控」解釋不了。是第三方拿著偷來的 key 在跑。追查 — 調查洩漏途徑
「那麼 key 是從哪裡漏出去的?」真正的調查從這裡開始。
為什麼一開始沒有發現
依照犯錯的順序寫下來,因為這些錯誤本身就是教訓。
太早認定原因
→ 在認定「是我的錯」或「是別人的錯」之前,先看資料。
grep 乾淨,差點就結束調查
掩蓋症狀感覺像修好了
grep 乾淨不代表沒事
即使任何檔案裡都沒有 key,漏洞也可能從正在執行的程序中取出環境變數。洩漏會發生在執行階段(RCE、HTTP 標頭),不只在檔案裡。詳見 什麼是 RCE。
實際原因:一個公開卻未修補的 CVSS 10.0
舊存取記錄中一個奇怪的特徵,很快就和威脅情報對上了。所使用的 Web 框架版本範圍,存在一個 公開的認證前 RCE(CVSS 10.0),而且已經在實際環境中被利用。它卻在未修補的狀態下運作了好幾個月。
- 這不是被動的 bug,而是攻擊者 在伺服器上執行程式碼並把環境帶走。
- 有一點限制了損害:執行 侷限在非特權容器內(主機 root 沒有被拿走)。入侵調查沒有發現常駐後門、挖礦程式或 C2,確認的影響是 secret 被竊。
- 由於內部 DB 可以連到,因此應對時假設 DB 內容也已外洩。
教訓:行為異常時,先懷疑已知的 CVE。不要假設是自己的 bug。
縮小洩漏範圍:那個 key 存放在哪裡?
grep 找不到時,就換個搜尋方式。列出被濫用的 key 曾寫在哪些地方,可能的洩漏途徑就會縮小。例如被濫用的 key 只存在於正式伺服器的環境變數中,就該懷疑正式伺服器或主機業者,而不是 Git 或瀏覽器。
| 洩漏位置 | 常見原因 | 如何檢查自己的環境 | 如何封堵 |
|---|---|---|---|
| Git 歷史/公開儲存庫 | 提交了 .env、把 key 寫死在程式碼中 | 用 gitleaks git 掃描完整歷史。查看 GitHub secret scanning 的警示 | 先撤銷,再在提交前攔截 |
| 送到瀏覽器的 JavaScript | 把 key 放在 NEXT_PUBLIC_ 變數中、直接從瀏覽器呼叫 API | 在建置輸出中搜尋 key 的前綴 | 把 API 呼叫移到伺服器端程式碼 |
| 公開的網站資料夾 | .env 或備份放在公開資料夾中 | 在自己的網站開啟 /.env,確認回傳 404 或 403 | 把機密檔案移到公開資料夾之外 |
| 記錄/錯誤頁面 | 開著除錯輸出上線、把請求標頭原樣寫入記錄 | 檢查正式環境的除錯設定,以及記錄中是否出現 Authorization 等 | 關閉除錯輸出,並在記錄中遮蔽機密 |
| 正在執行的伺服器 | 公開的漏洞(例如 RCE)未修補 | 比對實際執行的版本與公告中的修補版本 | 升級,然後更換環境中的所有 secret |
| 主機業者 | 業者端被入侵 | 查看業者的安全公告與電子郵件通知 | 以存檔後無法再檢視值的方式存放機密,並輪換 |
| 你的開發電腦 | 惡意套件或擴充功能 | 檢查安裝的套件或擴充功能版本是否符合已公開的入侵事件 | 移除該版本,並更換那台電腦上的 secret |
這次的案例屬於「正在執行的伺服器」那一列。最下面四列是不會在程式碼或 Git 中留下痕跡的途徑。OWASP 的 Secrets Management Cheat Sheet 也提醒,環境變數常常能被同一環境中的其他程序讀取,也可能被寫進記錄或傾印檔中。所以只要有一個 key 被濫用,就要把存放在同一處的所有 secret 視為已外洩。
分開使用 key,調查會更快
如果每個服務、每個環境(正式與開發)都使用不同的 key,光看被濫用的 key 就知道是表中的哪一列。到處共用同一個 key,就必須懷疑所有位置。如何拆分 key,請見 API key 被盜導致 OpenAI 帳號被停權時。
依實際執行的版本計算漏洞
檢查其他相依套件時,回報說「另外還有 8 個有漏洞的相依套件」。這也錯了。它們是依 package.json 中的 下限(^ caret 範圍)計算的。真正危險的,只有被固定版本而落後的 2 個相依套件。
確認自己實際執行的版本
查看 lock 檔案或正在執行的容器中實際解析出的版本。
# npm: see what's actually installed
npm ls next react react-dom
# inside a running container
docker exec <container> npm ls <package>這裡顯示的數字才是實際執行的版本,而不是 package.json 裡的 ^。
如何檢查自己的環境
無論你是剛注意到奇怪的帳單,還是什麼事都還沒發生,都可以依以下順序檢查。每一步都只是查看自己的帳戶和伺服器。
查看用了哪個 key、什麼時候用的
檢查送到瀏覽器的檔案
NEXT_PUBLIC_ 開頭的環境變數值,會在建置時被嵌入送往瀏覽器的 JavaScript。建置後,在輸出中搜尋 key 的前綴,例如 grep -rl "sk-" .next/static。在那裡找到的東西,任何人都讀得到。確認機密檔案無法從公開資料夾存取
curl -sI https://example.com/.env(把 example.com 換成你的網域),確認第一行的狀態碼不是 200(應該是 404 或 403)。更多內容請見 檢查公開目錄中的機密檔案。比對正式環境版本與公告
npm ls 查看版本。osv-scanner scan -L pnpm-lock.yaml 會檢查你的 lock 檔案是否包含有已知漏洞的版本。檢查記錄中是否有機密
gitleaks dir --redact,找出寫進記錄的 key。--redact 會讓找到的值本身不顯示在畫面上。檢查上限與警示
最初做了什麼(可重現的步驟)
立即撤銷被濫用的 key
把框架升級到修補版
輪換所有 secret
入侵調查
帳戶端的防禦
預防:自動監控 CVE
歸根結底,這次事故就是「人漏看了一個公開的 CVSS 10.0」。人工檢查一定會漏,自動監控就能抓到。
今天就能開始的相依套件掃描
免費就能開始,只要在 CI 裡加一個步驟。
# OSV scanner (Google) checks your lockfile
osv-scanner scan -L pnpm-lock.yaml
# On GitHub, enable Dependabot (repo Settings → Security)本站自己也依照這個教訓,監控自身相依套件的 CVE,我們自己也在做我們建議的事。
常見錯誤與修正方式
- 在仍有漏洞的伺服器上放新的 key:同一個途徑也會把新 key 偷走。立刻停用被濫用的 key,修正漏洞,然後再完成其餘 secret 的更換。
- 把預算警示當成上限:Google Cloud 說明只有警示的預算不會自動停止用量或支出。如果要讓它停下來,請加上會擋下用量的上限,例如 OpenAI 的每月硬性上限或 Anthropic 的 Workspace 支出上限。
- 把 AI API key 放進瀏覽器或行動 App:任何送到使用者裝置上的值都能被取出。請從你的伺服器呼叫 AI API,再讓瀏覽器呼叫你的伺服器。
- 從檔案或 Git 歷史中刪除就停手:已經複製走的人仍然可以使用。先撤銷,之後再清理歷史(見 為什麼 GitHub 上外洩的憑證仍然有效,英文)。
- 開發和正式環境使用同一個 key:無法分辨是哪一邊外洩,只好兩邊都停。請為每個環境核發不同的 key。
公開案例:環境變數從服務提供者那一側外洩
任何檔案搜尋都找不到的洩漏,也會發生在你自己的伺服器之外。2026 年 4 月 19 日,Vercel 公布其內部系統遭到未經授權存取。根據該公司的說明,一名員工使用的第三方 AI 工具遭入侵,導致部分客戶未標記為「sensitive」(儲存後無法再檢視值的設定)的環境變數被竊。
Vercel 建議客戶檢查並輪換未標記為 sensitive 的環境變數,並檢查活動記錄和近期的部署。該公司也表示,刪除專案或帳號並不足以消除風險。
處於這種情況的客戶,就算整天搜尋自己的程式碼和 Git,也找不到任何洩漏的痕跡。該採取的行動與本案例相同:更換存放在那個地方的所有 secret。步驟請見 Vercel 資安事件(2026 年 4 月)(英文)。
教訓
常見的錯誤
- 以「止住了帳單」就當作處理完成
- grep 乾淨就放心
- 在代理層掩蓋症狀,就覺得修好了
- 依
package.json的下限計算漏洞 - 只輪換確認被濫用的那一個 key
正確的防禦
- 把止住盜刷和封堵洩漏途徑當作兩件事,兩件都做
- 也懷疑執行階段的洩漏(RCE、標頭)
- 更新造成問題的框架
- 依實際執行的版本判斷
- 一旦外洩就更換整個 env
總結:帳單暴漲是症狀,原因是一直沒修補的 CVSS 10.0 RCE。正確的原因不是第一次就找到的,而是在多次出錯並重新檢查後才找到的。
接下來閱讀
- 外洩之後:GitHub 上外洩的 543,699 組憑證仍然有效:為什麼必須撤銷而不只是刪除(英文)
- 術語:什麼是 RCE · 什麼是 CVE · 什麼是 CVSS
- 對策:安全運作 Next.js(CVE 管理)
- 事故:.env 被公開給全世界的時候
- 基礎:.env 與 API key 的基礎(外洩後該做什麼)
- 預防:用 gitleaks 在提交前攔下機密
- 事故:API key 被盜導致 OpenAI 帳號被停權時
參考資料
- OWASP, "Secrets Management Cheat Sheet"(環境變數的風險、外洩時的應對):cheatsheetseries.owasp.org
- Next.js Docs, "How to use environment variables in Next.js"(
NEXT_PUBLIC_的嵌入):nextjs.org - OSV-Scanner, "Usage":google.github.io
- OpenAI, "Production best practices"(key 的處理、警示與上限):developers.openai.com
- Claude Help Center, "Creating and managing Workspaces"(Workspace 的上限與 key):support.claude.com
- Google Cloud, "Create, edit, or delete budgets and budget alerts":docs.cloud.google.com
- Vercel, "Vercel April 2026 security incident":vercel.com