.env 跟著 repo
一起上了 GitHub
為什麼現在很紅:兩年前某個 commit 裡的一把雲端金鑰,後來的版本早就刪了;repo 一改成公開,它就跟著 git 歷史一起被看見。
- 一句話結論
- 金鑰先寫進 .env 或程式碼「等一下再改」,某次 commit 就把它帶進 git 歷史;在最新版本刪掉,不等於消失。
- 他要的是什麼
- 他不用入侵你的伺服器。等你把 repo 改成公開,翻一下 git 歷史,兩年前那把金鑰還在原地。
- 你今天做一件事
- 今天就對最重要的 repo 跑一次完整歷史掃描,並確認 secret scanning 和 push protection 都開了。 直接去做 →
- 哪些能信
- 3 可證明2 推論2 缺口 每個主張都標了等級,缺口是誠實寫出來的。
- 破口速查
- 受害端一律去識別化;寫「未公開」代表公開資料查不到。
- 伺服器服務商
- 不適用(示意情境;涉及的是程式碼平台上的 repo 與雲端帳號)
- 網路服務商
- 不適用(同上)
- 漏洞出現的地方
- 程式碼層:金鑰寫在 .env 或程式碼常數裡被 commit,之後雖在最新版本刪除,但仍留在 git 歷史;repo 從私有改成公開,或被外包、離職者、遭入侵的帳號看到,金鑰就外洩
- 馬上自我檢測
git log --all --oneline -- .env gitleaks git --redact . git check-ignore -v .env 程式碼平台的 repo 設定 → 安全性頁,確認 secret scanning 與 push protection 是否已開啟- 漏洞修補方式
- 外洩時先到服務商後台撤銷或輪換金鑰,再清理 git;對所有 repo 跑完整歷史掃描;開啟 secret scanning 與 push protection,本機加 pre-commit hook;每個專案放 .env.example 並把 .env 列進 .gitignore;金鑰移到密鑰管理服務或部署平台環境變數;改公開前一律掃描完整歷史
有 15 分鐘再看:其餘 2 點
- repo 一改公開就可能被持續掃描;私有 repo 也不保險,外包、離職者、遭入侵的帳號都看得到。
- 外洩時先撤銷再清理;平時用 GitHub secret scanning、push protection 和 Gitleaks、TruffleHog 這類開源掃描器找出與擋下,再把金鑰移出程式碼。
「先寫死,等一下再改。」資料庫密碼、API key、雲端金鑰先放進 .env 或程式碼裡,某天 .gitignore 少一行、或 repo 從私有改成公開,金鑰就跟著程式碼一起出去了。在最新版本刪掉,git 歷史還記得。
本篇目錄
為了展示作品,把舊 repo 改成公開
假設有一家十人的 App 開發工作室,為了展示作品,把一個舊專案的 repo 從私有改成公開。幾天後雲端帳單出現異常,有人用他們的金鑰開了大量運算資源。追查發現,金鑰在兩年前的某個 commit 裡;後來的版本雖然刪掉了,但 git 歷史還留著。
先說清楚,這是示意情境,不是真實事件,工作室與經過都是為了說明而設。但「先寫死,等一下再改」幾乎每個工程師都說過:資料庫密碼、第三方 API key、雲端金鑰,先放進 .env 或直接寫在程式碼裡,功能跑起來最重要。然後某天 .gitignore 少了一行,或是有人把 repo 改成公開。
這篇要講的只有一件事:金鑰一旦進了 git,就要當它已經出去了。
git 會記住一切
第一層是歷史。在最新版本刪掉,不等於消失;任何一個舊 commit 都還能把它翻出來。
第二層是公開。公開 repo 會被持續掃描,金鑰一公開,就可能很快被找到並濫用。
第三層是私有也不保險。外包、離職者、被入侵的帳號,都能看到私有 repo 的完整歷史。
最後是擴散。一把金鑰往往用在很多地方,外洩後要找出所有用到它的位置,才能安全撤換;找不齊,就只能邊換邊等哪裡壞掉。
這篇示意情境裡,跟著 repo 出去的是什麼
這是示意情境,沒有實際外洩;下表列的是這類事件裡最常跟著 repo 一起出去的憑證,讓你知道一個 .env 裡可能放了幾把鑰匙。
| 服務 | 憑證類型 | 用途 | 存在形式 | 放在哪 | 權限範圍 | 狀態 |
|---|---|---|---|---|---|---|
| 雲端帳號(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;既有用法仍可使用,長期導入時請把維護狀態納入考量。
這篇提到的項目要花多少錢
來源沒有列異常帳單金額與商用方案價格,表裡照實標成未公開或估算。
| 項目 | 用量 | 單價 | 期間 | US$ | NT$ | 舉證 |
|---|---|---|---|---|---|---|
| 示意情境中被開出的大量運算資源 | 未公開 | 未公開 | repo 公開後數天 | — | — | 示意情境,無實際帳單 |
| 對所有 repo 跑一次完整歷史掃描 估 | 工程師一人 | 估 2–4 小時 | 一次性 | — | — | 本站依流程估算,依 repo 數量而定 |
| Gitleaks/TruffleHog | 開源 | 軟體免費 | — | 0.00 | 0 | 各專案 GitHub 頁 |
| GitHub secret scanning(私有 repo) | 依方案 | 可用性與費用依方案 | — | — | — | 官方文件註明依方案而定,本文未取得價格 |
| 攻擊者撿到公開 repo 裡的金鑰 估 | 翻 git 歷史 | 幾乎零成本 | — | — | — | 本站推估;無攻擊端資料 |
| 合計(僅可證明列) | 0.00 | 0 | ||||
攻擊者那一列幾乎是零,防守那一列卻是一整個晚上的撤換與追查;這就是為什麼要在推送前擋下來。
你可以學到什麼
站在攻擊者那一側看,公開 repo 的 git 歷史是一個不會過期的寶箱。他不用猜密碼,也不用打穿任何系統,只要在你改成公開的那幾天之內翻到那個舊 commit。歷史不替任何人守密。
- 01金鑰在最新版本刪掉不等於消失,git 歷史裡的每個舊 commit 都還能把它翻出來。
- 02外洩時先到服務商後台撤銷或輪換金鑰,再清理 git;改寫歷史收不回已經被複製的內容。
- 03GitHub secret scanning 會掃所有分支的完整歷史,push protection 能在推送前擋下可辨識的密鑰,兩者都該先打開。
- 04每個專案放只有欄位名稱的 .env.example,並把 .env 列進 .gitignore,讓「先寫死」沒有機會被 commit。
- 05repo 改成公開前,一律先掃描完整歷史;這一步做了,示意情境裡的那筆帳單就不會出現。
- 01.env這篇的主角:最常放明文金鑰、也最常被誤 commit 的檔案。
- 02git 歷史刪掉的檔案還在舊 commit 裡,掃描與清理都要看完整歷史。
- 03Secret ScanningGitHub 內建的完整歷史掃描,先開它就能找出多數已知格式的密鑰。
- 04Push Protection在推送前擋下密鑰,比外洩後補救便宜得多。
- 05.gitignore少一行就會把 .env 帶進 repo,是最小也最常見的破口。
- 06.env.example只有欄位名稱的範本,讓新成員知道要設什麼,又不會帶出值。
- 07Gitleaks可以自己跑的開源掃描器,git 與 dir 指令分別掃 repo 和本機目錄;注意目前只發布安全修補。
- 08TruffleHog開源掃描器,能找出並驗證外洩憑證是否仍有效。
- 09pre-commit hook在本機 commit 前就攔下,補上平台功能管不到的那一段。
- 10密鑰管理(Secrets Manager)把金鑰移出程式碼的去處,回答「金鑰從哪裡來」。
全部唯讀,不改任何設定;做完你會知道自己有沒有同樣的破口。
- 在最重要的 repo 裡,列出歷史上所有動過 .env 的 commit(唯讀)
git log --all --oneline -- .env看到什麼代表什麼:有任何輸出,代表 .env 曾經被 commit 過,裡面的金鑰要當作已外洩處理。 - 確認 .env 有被 .gitignore 忽略(唯讀)
git check-ignore -v .env看到什麼代表什麼:印出是哪個 .gitignore 的哪一行在忽略它;沒有輸出就代表沒被忽略。 - 已安裝 Gitleaks 的話,掃一次 repo 的完整歷史,結果遮蔽顯示(唯讀)
gitleaks git --redact .看到什麼代表什麼:列出的每一筆都要到服務商後台確認是否仍有效;沒有發現也不代表沒有,掃描器會漏判。 - 打開程式碼平台該 repo 的設定 → 安全性頁,看 secret scanning 與 push protection 的狀態(唯讀,不要改)看到什麼代表什麼:顯示未開啟的,就是今天可以先打開的兩個開關。
- 列出公司所有公開 repo,標出哪些是從私有改成公開的(不用指令)看到什麼代表什麼:改過公開的 repo,都要確認改之前有沒有掃過完整歷史。
揭露:本文未受任何廠商贊助,提及的產品僅為舉例。
環境背景、時間線、攻擊鏈對照 ATT&CK、關鍵證據、根因、處置與檢討都在完整版,約 7 分鐘。
來源
- GitHub Docs:關於 secret scanning
- GitHub Docs:關於 push protection
- Gitleaks(開源密鑰掃描器)
- TruffleHog(開源憑證掃描器)
- Infisical(開源密鑰管理)
- OpenBao(開源 Vault 分支)
- IBM 新聞稿:完成收購 HashiCorp
本文為公開報導與官方公告的整理與拆解,非第一手鑑識;證據等級依來源可信度標註。
本文由 AI 團隊協助研究與撰寫,經隱私與事實檢查後發布;證據等級依來源可信度標註。
複製下面這段,貼給你在用的 AI 工具(Claude Code、Cursor、Copilot、ChatGPT 都可以)。它會用本篇的破口位置、自我檢測與學習重點,對你的專案或環境做一次資安掃描,列出潛在問題與建議的調整或加強方式。
預設是唯讀:只發現問題、提出建議,不會改你任何檔案或設定。
M2 身分與金鑰 · 想在自己的環境做一次同樣的盤點,從 自我盤點 開始。
每篇新文章一封:三點摘要、關鍵字定義、今天能做的一件事。或先 加入會員 記錄你完成的任務。