作者分析

.env 跟著 repo一起上了 GitHub

「先寫死,等一下再改。」資料庫密碼、API key、雲端金鑰先放進 .env 或程式碼裡,某天 .gitignore 少一行、或 repo 從私有改成公開,金鑰就跟著程式碼一起出去了。在最新版本刪掉,git 歷史還記得。

30 秒看懂
  • 金鑰先寫進 .env 或程式碼「等一下再改」,某次 commit 就把它帶進 git 歷史;在最新版本刪掉,不等於消失。
  • repo 一改公開就可能被持續掃描;私有 repo 也不保險,外包、離職者、遭入侵的帳號都看得到。
  • 外洩時先撤銷再清理;平時用 GitHub secret scanning、push protection 和 Gitleaks、TruffleHog 這類開源掃描器找出與擋下,再把金鑰移出程式碼。

為了展示作品,把舊 repo 改成公開

假設有一家十人的 App 開發工作室,為了展示作品,把一個舊專案的 repo 從私有改成公開。幾天後雲端帳單出現異常,有人用他們的金鑰開了大量運算資源。追查發現,金鑰在兩年前的某個 commit 裡;後來的版本雖然刪掉了,但 git 歷史還留著。

先說清楚,這是示意情境,不是真實事件,工作室與經過都是為了說明而設。但「先寫死,等一下再改」幾乎每個工程師都說過:資料庫密碼、第三方 API key、雲端金鑰,先放進 .env 或直接寫在程式碼裡,功能跑起來最重要。然後某天 .gitignore 少了一行,或是有人把 repo 改成公開。

這篇要講的只有一件事:金鑰一旦進了 git,就要當它已經出去了。

git 會記住一切

第一層是歷史。在最新版本刪掉,不等於消失;任何一個舊 commit 都還能把它翻出來。

第二層是公開。公開 repo 會被持續掃描,金鑰一公開,就可能很快被找到並濫用。

第三層是私有也不保險。外包、離職者、被入侵的帳號,都能看到私有 repo 的完整歷史。

最後是擴散。一把金鑰往往用在很多地方,外洩後要找出所有用到它的位置,才能安全撤換;找不齊,就只能邊換邊等哪裡壞掉。

這篇示意情境裡,跟著 repo 出去的是什麼

這是示意情境,沒有實際外洩;下表列的是這類事件裡最常跟著 repo 一起出去的憑證,讓你知道一個 .env 裡可能放了幾把鑰匙。

leaked credentials這次外洩了什麼:服務、用途、存在形式
服務憑證類型用途存在形式放在哪權限範圍狀態
雲端帳號(IAM)長期存取金鑰讓程式部署與操作雲端資源明文寫在 .env 或程式碼常數裡兩年前的某個 commit;最新版本已刪,git 歷史仍在至少能建立運算資源(示意情境中被拿來開大量運算資源)示意情境;撤銷前持續有效
同一個 .env 裡的其他密鑰(資料庫密碼、第三方 API key)密碼/API key連資料庫、呼叫第三方服務明文環境變數同一個 commit 的可能性高,但未逐一確認依各服務而定是否一起外洩無法確認
憑證本身已遮蔽或失效;這裡只說明它是哪個服務、拿來做什麼、以什麼形式存放,讓你能對照自己的環境。

第二列標成缺口,是因為「同一個檔案裡的其他鑰匙有沒有也被拿走」,在示意情境裡沒有人查過。

能證明的、只能推論的

這篇是觀點文,情境是示意的,所以能證明的只有官方文件與官方公告寫明的事;情境裡的經過只能算推論,不知道的就寫成缺口。

不花錢就能做的流程

到這裡,其實已經足夠理解問題在哪。如果你想直接動手,可以跳到「今天就可以來試試」;下面是完整的六步流程。

第一步,外洩時,先撤銷再清理。 第一步是到服務商後台撤銷或輪換金鑰,而不是先改 git 歷史。改寫歷史無法收回已經被複製的內容。

第二步,掃一次所有 repo。 GitHub 的 secret scanning 會掃描所有分支的完整歷史;開源的 Gitleaks(git、dir 指令可以掃 repo 和本機目錄)、TruffleHog(README 說可以找出、驗證並分析外洩憑證)也能自己跑。

第三步,在推送前擋下來。 開啟 GitHub 的 push protection;本機也可以用 pre-commit hook 搭配開源掃描器。

第四步,標準化 .gitignore 和範本。 每個專案都放 .env.example,只有欄位名稱,沒有值;.env 一律列進 .gitignore。範本長這樣:

# .env.example:只放欄位名稱,值留空
DATABASE_URL=
PAYMENT_API_KEY=
CLOUD_ACCESS_KEY_ID=
CLOUD_SECRET_ACCESS_KEY=

第五步,金鑰移出程式碼。 正式環境改從密鑰管理服務或部署平台的環境變數讀取。

第六步,公開 repo 前做檢查。 把「掃描完整歷史」列為改成公開前的必要步驟,寫進一張 go-public-checklist-g5,沒勾完不改公開。

什麼時候不需要買工具

GitHub 平台內建功能加上開源掃描器,已經涵蓋「找出」和「擋下」兩件事。密鑰管理平台的價值在於「金鑰從哪裡來」,適合有多個環境和多人協作的團隊。

市面上大致分五類。程式碼平台內建:GitHub secret scanning 和 push protection(私有 repo 的可用性依方案而定)。開源掃描器:Gitleaks、TruffleHog。雲端原生密鑰管理:AWS Secrets Manager、Google Cloud Secret Manager。開源密鑰管理:Infisical(GitHub 上開源)、OpenBao(Linux 基金會 OpenSSF 下的開源 Vault 分支,MPL-2.0 授權)。商用密鑰管理:例如 HashiCorp Vault(現為 IBM 旗下,授權已改為 BSL)、1Password 的開發者密鑰管理(官網說可以在程式碼裡用引用取代金鑰本身,並有 GitHub Actions 等 CI/CD 整合)。

限制也要一起看:掃描器有誤判也有漏判,自訂格式的密鑰可能偵測不到;密鑰管理平台要改程式的讀取方式,導入有成本。選的時候問三件事:程式碼放在哪(在 GitHub 的話,先開內建功能)、基礎架構是不是主要在單一雲端(是的話,雲端原生的密鑰管理最省事)、有沒有自架能力(OpenBao 這類自架方案需要維運能力)。

後續更新兩則。2026/07/28,1Password 宣布 1Password Environments 正式提供,用於管理和共享專案的環境變數設定。2026/10/17 查證時,Gitleaks 的 README 寫明專案已「功能完整」,之後只發布安全修補,作者轉而開發 Betterleaks;既有用法仍可使用,長期導入時請把維護狀態納入考量。

這篇提到的項目要花多少錢

來源沒有列異常帳單金額與商用方案價格,表裡照實標成未公開或估算。

cost金鑰跟著 repo 出去的成本
項目用量單價期間US$NT$舉證
示意情境中被開出的大量運算資源未公開未公開repo 公開後數天——示意情境,無實際帳單
對所有 repo 跑一次完整歷史掃描
估
工程師一人估 2–4 小時一次性——本站依流程估算,依 repo 數量而定
Gitleaks/TruffleHog開源軟體免費—0.000各專案 GitHub 頁
GitHub secret scanning(私有 repo)依方案可用性與費用依方案———官方文件註明依方案而定,本文未取得價格
攻擊者撿到公開 repo 裡的金鑰
估
翻 git 歷史幾乎零成本———本站推估;無攻擊端資料
合計(僅可證明列)0.000
匯率假設 1 USD ≈ NT$32。單價為公開牌價或帳單實付,來源:本站估算;GitHub Docs、各開源專案頁(2026-10-17 讀取)。「舉證」欄標明每筆損失的證據來源與等級( 可證明 推論 缺口)。

攻擊者那一列幾乎是零,防守那一列卻是一整個晚上的撤換與追查;這就是為什麼要在推送前擋下來。

你可以學到什麼

站在攻擊者那一側看,公開 repo 的 git 歷史是一個不會過期的寶箱。他不用猜密碼,也不用打穿任何系統,只要在你改成公開的那幾天之內翻到那個舊 commit。歷史不替任何人守密。

lessons本案的 5 個教訓
  1. 01
    金鑰在最新版本刪掉不等於消失,git 歷史裡的每個舊 commit 都還能把它翻出來。
  2. 02
    外洩時先到服務商後台撤銷或輪換金鑰,再清理 git;改寫歷史收不回已經被複製的內容。
  3. 03
    GitHub secret scanning 會掃所有分支的完整歷史,push protection 能在推送前擋下可辨識的密鑰,兩者都該先打開。
  4. 04
    每個專案放只有欄位名稱的 .env.example,並把 .env 列進 .gitignore,讓「先寫死」沒有機會被 commit。
  5. 05
    repo 改成公開前,一律先掃描完整歷史;這一步做了,示意情境裡的那筆帳單就不會出現。
keywords重點學習:最需要學習的 10 個關鍵字10 個
  1. 01
    .env
    這篇的主角:最常放明文金鑰、也最常被誤 commit 的檔案。
  2. 02
    git 歷史
    刪掉的檔案還在舊 commit 裡,掃描與清理都要看完整歷史。
  3. 03
    Secret Scanning
    GitHub 內建的完整歷史掃描,先開它就能找出多數已知格式的密鑰。
  4. 04
    Push Protection
    在推送前擋下密鑰,比外洩後補救便宜得多。
  5. 05
    .gitignore
    少一行就會把 .env 帶進 repo,是最小也最常見的破口。
  6. 06
    .env.example
    只有欄位名稱的範本,讓新成員知道要設什麼,又不會帶出值。
  7. 07
    Gitleaks
    可以自己跑的開源掃描器,git 與 dir 指令分別掃 repo 和本機目錄;注意目前只發布安全修補。
  8. 08
    TruffleHog
    開源掃描器,能找出並驗證外洩憑證是否仍有效。
  9. 09
    pre-commit hook
    在本機 commit 前就攔下,補上平台功能管不到的那一段。
  10. 10
    密鑰管理(Secrets Manager)
    把金鑰移出程式碼的去處,回答「金鑰從哪裡來」。
try today今天就可以來試試約 15 分鐘

全部唯讀,不改任何設定;做完你會知道自己有沒有同樣的破口。

  1. 在最重要的 repo 裡,列出歷史上所有動過 .env 的 commit(唯讀)
    git log --all --oneline -- .env
    看到什麼代表什麼:有任何輸出,代表 .env 曾經被 commit 過,裡面的金鑰要當作已外洩處理。
  2. 確認 .env 有被 .gitignore 忽略(唯讀)
    git check-ignore -v .env
    看到什麼代表什麼:印出是哪個 .gitignore 的哪一行在忽略它;沒有輸出就代表沒被忽略。
  3. 已安裝 Gitleaks 的話,掃一次 repo 的完整歷史,結果遮蔽顯示(唯讀)
    gitleaks git --redact .
    看到什麼代表什麼:列出的每一筆都要到服務商後台確認是否仍有效;沒有發現也不代表沒有,掃描器會漏判。
  4. 打開程式碼平台該 repo 的設定 → 安全性頁,看 secret scanning 與 push protection 的狀態(唯讀,不要改)
    看到什麼代表什麼:顯示未開啟的,就是今天可以先打開的兩個開關。
  5. 列出公司所有公開 repo,標出哪些是從私有改成公開的(不用指令)
    看到什麼代表什麼:改過公開的 repo,都要確認改之前有沒有掃過完整歷史。

揭露:本文未受任何廠商贊助,提及的產品僅為舉例。

來源

分享
精選拆解直送信箱

每篇新文章一封:三點摘要、關鍵字定義、今天能做的一件事。

延伸閱讀

作者分析

AI 工具已經從瀏覽器,跑進你的電腦裡了

AI 編程工具在本機執行指令,MCP server 在背景運作,代理可以讀寫檔案、呼叫 API。這些東西跑在員工電腦上,不在 IT 看得到的雲端服務清單裡。這篇講本機影子 AI 怎麼盤點、設定檔裡的金鑰怎麼收。

  • 2026/10/08
  • 每日拆解
作者分析

AI 寫程式助手需要金鑰,但模型不該看到它

讓 AI 編程工具直接跑指令、部署、呼叫 API,最直接的方法就是把金鑰貼進對話。工作完成了,那把金鑰也進了對話紀錄、工具的上下文,甚至某個你沒看過的記錄檔。這篇講怎麼讓工具用得到金鑰,模型卻看不到。

  • 2026/08/03
  • 每日拆解
作者分析

換一把金鑰,要改六個地方

顧問說金鑰要定期輪換,工程主管點頭,然後開始盤算:正式環境、測試環境、CI/CD、兩個微服務、某位同事本機的腳本,漏改一處就是半夜壞掉。結論是「下一季再說」。這篇講怎麼讓輪換從六處變成一處。

  • 2026/07/25
  • 每日拆解