「都說安全很重要,可到底該先從哪兒下手?」——這就是寫給這樣的個人開發者的、最最開始的第一步。不是為了嚇唬你,而是只把從今天就能動手的事,按順序講清楚。
為什麼要從秘密的值(.env 和 API key)開始
真實發生的事故,很多都不是什麼高級攻擊,而是從**「秘密的值洩漏了」**開始的。本站拿來作為出發點的兩個案例,也都是秘密洩漏:
也就是說,只要能守住秘密的值,常見事故裡的絕大多數就都能防住。所以我們就從這裡開始。
.env 是什麼(30 秒講完)
.env(點 env)是把應用程式要用的秘密的值彙總到一起的設定檔。裡面裝著資料庫的密碼、外部服務的 API key、給工作階段加密用的金鑰等等。正因為秘密都集中在一個檔案裡,一旦洩漏就會一口氣全部外洩,所以要最優先地守好它(→ 術語:.env 是什麼)。
秘密的值可以放在哪裡(.env、環境變數、秘密管理服務)
.env 並不是放秘密的唯一地方。請依規模和用途選擇。
| 位置 | 適合 | 要注意的地方 |
|---|---|---|
.env 檔 | 本機開發、單台小型伺服器 | 排除在 Git 之外,也不要放進公開目錄 |
| 主機的環境變數設定(在管理介面輸入) | 正式環境 | 限制能存取管理介面的人;別讓值出現在建置日誌裡 |
| CI(自動測試與部署)的 secrets | GitHub Actions 等自動化 | 在 CI 中執行的外部工具看得到這些值;只傳入具備必要權限的 key |
| 秘密管理服務 | 多人或多台伺服器 | 讀取權限保持最小,並定期輪換 |
關於 CI 的 secrets,2021 年的 Codecov 事件是很好的參考。在 CI 中執行的外部工具遭到竄改,存放在 CI 環境變數中的秘密被拿走了(→ Codecov 事件)。
可以公開的 key 和不能公開的 key
有些 API key 本來就是要放在瀏覽器或行動 App 裡的,有些則只能用在伺服器上。送到瀏覽器的任何東西,都要假設任何人都讀得到。
| 例子 | 可以公開嗎? | 理由 |
|---|---|---|
Stripe 可公開金鑰(以 pk_ 開頭) | 可以 | 文件說明可以放在前端程式碼中;它無法進行建立付款這類敏感操作 |
Stripe 秘密金鑰(sk_)或受限金鑰(rk_) | 不行 | 能進行伺服器端的操作;文件建議使用縮小權限的受限金鑰 |
Next.js 中帶 NEXT_PUBLIC_ 的環境變數 | 會被公開 | 它的值會在建置時被內嵌到瀏覽器的 JavaScript |
Next.js 中不帶 NEXT_PUBLIC_ 的環境變數 | 不會公開(只在伺服器) | 秘密的值放這裡 |
拿不準時,請查看該服務的官方文件,確認這個 key 能不能在用戶端使用。如果文件沒寫,就當作不能公開的 key 處理。
API key 洩漏後會發生什麼
API key 是「讓外部服務把呼叫者當成你的一串字串」。它一旦落到別人手裡,損害會這樣擴大。
被偷走的 key 往往會有時間差地被轉賣、濫用,「洩漏剛發生時啥也沒有→過些天帳單突然飆升」是很典型的套路。所以「等發現了再說」就晚了,關鍵是從一開始就別讓它洩漏。
key 可能已經洩漏時該怎麼做
把 key 從程式碼裡刪掉,並不能讓它失效。GitGuardian 2025 年的報告發現,2022 年在公開儲存庫中外洩的秘密,有 70% 到報告發布時仍然有效。請按以下順序處理。
- 在發行方的管理介面撤銷或輪換這個 key。如果服務提供寬限期,請讓外洩的 key「立即」失效
- 在正式環境設定新的 key,並確認應用程式正常運作
- 查看發行方的使用紀錄和帳單,確認有沒有不是你做的活動
- 從檔案和 Git 歷史中移除可以等撤銷之後再做(光是刪除,擋不住已經拿到副本的人)
詳情請見 在 GitHub 上公開的 54 萬組憑證仍然有效。GitHub 預設開啟「push protection」,會阻止個人帳號把秘密推送到公開儲存庫,但它並不能抓到所有種類的秘密。
從今天就能做的 4 個防護辦法
不需要什麼難的東西。先做好這 4 件就行。
不公開
別把 .env 和應用程式本體放到 Web 上能看見的目錄(公開根目錄)裡。對外公開的只能是 public/。在虛擬主機上的具體操作步驟見 這篇文章。
不提交
在 .gitignore 裡加上 .env*(!.env.example 可以放行)。只共享不帶值的範例。一旦提交過就能從歷史裡還原出來,所以從一開始就別放進去。
洩漏就全部更換
不是只換一個,而是把有可能洩漏的鑰匙全部輪換。優先順序是「外部 API · OAuth → 加密金鑰 → 郵件 → DB」。按「已經被看到了」的前提來處理。
自查
偶爾確認一下自己的網站,看 /.env 能不能從外面打開(見下)。
自查(只對自己擁有的網域做)
# 回傳 200 且帶正文就是被公開了。403/404 則暫時沒問題
curl -sI https://你的網域/.env | head -1
curl -sI https://你的網域/.git/config | head -1養成每次上線都確認一遍的習慣,就能盡早發現放置位置的失誤。
除了從外部看得到的部分,也請檢查 Git 和建置產物(在你自己的儲存庫中執行)。
# .env 是否曾經被提交過?(只要有輸出,就代表某個時間點提交過)
git log --all --oneline -- .env
# Git 目前追蹤的 env 類檔案(.env.example 以外的都要確認)
git ls-files | grep -i "env"要確認沒有 key 被送到瀏覽器,請在建置輸出資料夾(Next.js 是 .next/static)中搜尋 key 的前幾個字元。也可以在公開前把程式碼貼到 機密外洩掃描 檢查(不會傳送到任何地方)。
容易犯的錯 vs 正確的第一步
容易犯
- 反正先跑起來了,就把
.env直接放在 public 下 - 想著「以後再刪」,把
.env提交進 git 一次 - 只換那一個看到被濫用的鑰匙
正確的第一步
- 本體放在公開根目錄之外,只公開
public/ - 從一開始就用
.gitignore把.env排除掉 - 洩漏了就把 env 裡的鑰匙全部輪換
下一步
走到這一步,接下來就是「自己技術棧裡危險的預設設定」和「不在已公開漏洞(CVE)上落後的機制」了。別著急,一個一個來。
出處
- Next.js「How to use environment variables in Next.js」(16.x)— nextjs.org
- Stripe Docs「API keys」— docs.stripe.com
- Laravel 13.x「Configuration」(Environment File Security)— laravel.com
- GitHub Docs「About push protection」— docs.github.com
- GitGuardian「The State of Secrets Sprawl 2025」— blog.gitguardian.com
接著讀
- 外洩之後:在 GitHub 上公開的 543,699 組憑證仍然有效:為什麼必須撤銷,而不只是刪除
- Gyazo 外洩事件(2026 年 9 月):外洩了什麼,使用者今天該做什麼
- 術語:.env 是什麼 / CVE 是什麼 / RCE 是什麼
- 防護:在虛擬主機上別讓 .env 被公開 / Next.js 的 CVE 跟進
- 事故:API key 被偷走後遭盜刷的事
- 事故:用偷來的 key 導致帳號被封的事
- 供應鏈:一次 npm install 就偷走你的憑證時(別人的程式碼在存放你 key 的環境中執行)