Velocity Curve
預約通話
← 回到案例總表
SECSEC-01  ·  資安

一把留在程式專案保存歷史裡的金鑰,持續計費了好幾個月,沒有任何東西替它封頂,直到一名學生的帳單累積到 55,444.78 USD

已報導損失$55,444.78 · 14,200+ 次請求 · 100 % 失敗
判定

一枚在 GitHub 上某一次 commit 中被公開的 API key,在大約三個月的時間裡始終完全可用、也完全會計費,沒有任何上限、也沒有任何能真正送達的告警把不斷累積的費用呈現出來,直到另一名開發者通知了這名學生帳戶持有人。

事件經過

一名學生用學生電子郵件地址註冊了一個雲端帳戶,並在 300 USD 的免費額度內使用。其中約 80 USD 花在小型的學習練習上,還剩下 220 USD 以上。初夏時,一枚 API key 被推送到 GitHub 上一個帳戶持有人以為是私有的程式專案裡,就這樣留在該專案保存下來的歷史紀錄中。

6 月接著來了一張 732 USD 的帳單。這筆錢沒有被收走,因為帳上登記的付款卡片早已過期,而付款失敗的通知也沒有送到帳戶持有人的手機上。8 月又來了一張超過 31,000 USD 的帳單,9 月初再累積了 21,000 USD 的費用。暑假期間,那個學生電子郵件地址沒有人在讀。

9 月,發現這枚外洩金鑰的開發者透過 GitHub 發出通知,說這枚金鑰已經公開很久,而且有其他人正在使用它。帳戶持有人打開帳戶,看到總額 55,444.78 USD,立刻撤銷了這枚金鑰。接下來的 2 天內,使用這枚金鑰的不明人士又發出超過 14,200 次使用嘗試,失敗率 100 %。

帳戶持有人聯絡了雲端服務商的計費支援、報了警,並提供使用紀錄、GitHub 連結、螢幕截圖與其他文件,說明帳上登記的卡片已經過期。案件經過審查,得到的答覆是這些費用依然有效:不取消、不變更。接著又收到通知,表示若未在 10 天內付清餘額,這筆債務將移交催收機構,並可能加上額外費用。在帳戶持有人所在的國家,平均每日收入約為 15 USD。

6 月 6 日帳戶持有人不小心把 API key 推送到 GitHub,當時以為那個 repository 是私有的;金鑰出現在其中一次 commit 裡,沒有被注意到。
6 月開出第一張帳單,金額 732 USD;這筆錢沒有被收取,因為帳上登記的卡片到期日是 2025 年 7 月。
8 月開出第二張帳單,金額超過 31,000 USD。
9 月 1 日至 7 日又被收取了 21,000 USD。
9 月 7 日另一位 GitHub 使用者發出通知,說這枚金鑰已經公開很久,而且有其他人正在濫用它。
通知之後帳戶持有人檢查帳戶、看到總額,立刻撤銷了那枚 Gemini API key。
撤銷後的 2 天內仍有超過 14,200 次請求使用這枚金鑰送出,失敗率 100 %。
發現之後帳戶持有人聯絡 Google Cloud Billing Support、報了警,並提供使用紀錄、GitHub 連結、螢幕截圖與其他文件,並說明帳上登記的卡片已經過期。
審查之後案件經過審查,最終答覆是這些費用依然有效;不取消、不變更。
最終答覆之後收到通知,表示若未在 10 天內付清這筆債務,將移交催收機構,並可能加上額外費用。
9 月 25 日在與計費專員溝通之後,案件再次被審查,全部未付餘額獲得完全免除。

主要來源——S1:https://www.reddit.com/r/googlecloud/comments/1noctxi/student_hit_with_a_5544478_google_cloud_bill存檔快照

損失成因

  • 一枚有效的金鑰可以毫無天花板地不斷累加帳單,而不斷長大的費用從來沒有從一個平靜的普通帳戶中被凸顯出來 一枚有效金鑰的每一次呼叫都會計費,因此損失的規模是由帳戶上的天花板、以及警告多快落到有人在看的地方所決定。在沒有任何上限生效、計費信件又寄到暑假期間沒人閱讀的信箱的情況下,總額可以用機器的速度攀升好幾個月,才被人看見。技術名稱:無支出上限的計費憑證(uncapped billable credential)
  • 一枚進到程式專案保存歷史裡的金鑰持續有效了好幾個月,沒有東西撤銷它,也沒有東西提示它可被讀取 程式碼分享網站會保留專案的每一個舊版本,因此金鑰在目前檔案背後的歷史紀錄裡仍然可以被讀出來。一枚本身不會自動到期的金鑰,在有人撤銷它之前,可用程度和它被寫下的那天完全一樣,這就是為什麼這次暴露從初夏一直延續到 9 月有陌生人來信。技術名稱:版本歷史中的長效密鑰(long-lived secret in version history)

判讀

金鑰外洩是一次性的失誤;一個沒有上限的帳戶,會把它變成每天沒人注意就繼續長大的帳單。決定這次損失規模的不是那次失手,而是失手之後那幾個月無人節制的支出,以及第一個察覺異常的人是陌生人,而不是開發者本人。

自我查核

只有你能回答的

  • 當你註冊並拿到免費額度時,你是不是把那筆額度當成這個帳戶最多能花的錢?曾經有任何東西告訴你並非如此嗎?
  • 如果現在就有別人在使用你的某一枚金鑰,在事情變得無法挽回之前,你最多能吸收多大的一筆帳單?你曾經替這件事寫下一個數字嗎?
  • 你每週真正會讀的是哪一個信箱、哪一支電話?你當初以為「錢從你帳戶流出」的消息,會送到那裡嗎?

你的 AI 可以幫你查的

只做報告,不要修改任何程式碼或設定。每一項發現都要附上檔案或主控台的參照位置;凡是從你能看到的資訊無法判定的,請明白說出來。

  1. 列出我擁有的每一個雲端計費帳戶。逐一report:是否設定了預算或支出上限、金額是多少、達到上限時會停止付費使用還是只發出訊息,以及計費告警與付款失敗通知會送到哪些電子郵件地址和電話號碼;並附上主控台參照位置。
  2. 列出我帳戶上每一枚有效的 API key,包含建立日期、最後使用時間、可以呼叫哪些付費服務、是否被限制在特定服務或特定呼叫來源,以及是否有任何各服務的用量配額。標示出任何沒有限制、也沒有到期時間的金鑰,並附上主控台參照位置。
  3. 搜尋我擁有的每一個 repository 的完整 commit 歷史、所有分支與已刪除的檔案,找出 API key 與其他密鑰。逐一報告每個命中結果,附上檔案與 commit 參照位置,說明每一枚命中的金鑰是否仍然有效,並報告每一個 repository 是否已開啟 secret scanning 與推送保護(push protection)。

預防方式

為帳戶設一個天花板,並確認警報會落在你真的會看的地方

用書面向經營你雲端帳戶的公司問一個問題:當我的帳戶超過我設定的上限時,付費使用會停止,還是會繼續運作、繼續向我計費?把答覆留存下來。然後把你真的會讀的電子郵件地址和手機號碼放進帳戶,並保持付款卡片資料有效,因為一筆無聲失敗的付款,會拿掉大多數人唯一依賴的那個訊號。把免費額度當成起始餘額,永遠不要當成一道牆。

假設任何曾經接近共用程式專案的東西都已經公開,並照這個前提撤銷金鑰

撤銷並更換每一枚曾經放進程式專案裡的金鑰,包含你認為是私有的專案。刪掉檔案並不夠;網站會保留它背後的舊版本,而金鑰在有人撤銷它之前一直有效。為你擁有的每一個專案開啟程式碼託管平台的 secret scanning 與推送時封鎖(block-on-push)設定,並且把會花錢的金鑰,從你正在學習或做實驗的東西上全部拿掉。

外洩的金鑰只讓你付一次代價;沒有天花板的帳戶,每天都會讓你有損失。