資安指南

在虛擬主機上避免 .env 被公開的目錄配置與設定

在共用虛擬主機部署 Laravel 等應用程式時,如何讓 .env、.git、vendor 無法被外部讀取:把應用程式放在公開根目錄之外,只公開 public/。附目錄配置、.htaccess 緊急處置與自我檢查。

發布於 2026-06-07 更新於 2026-10-06 閱讀時間 4 分鐘
本頁內容

對象:在共用虛擬主機(Apache/.htaccess 環境)上運行 Laravel 等「只應公開 public/」的應用程式的人。這是從真實事故(→ .env 全公開的故事)中整理出來、用來保護自己部署環境的步驟。

本站的看法:這不是「個人的疏忽」

虛擬主機早期的文化是把所有東西都放在 public_html 底下。以只公開 public/ 為前提的 Laravel 進入了這種環境,危險的目錄配置就成了事實上的標準,連業者自己的文件也把專案放在 public_html 裡。所以對策要靠流程,而不是注意力:改變你的部署預設值。

用圖確認正確的目錄配置

✗ 危險(主體放在公開根目錄之下)

public_html/
├─ .env   ← 能被讀到!
├─ .git/  vendor/
└─ public/

✓ 安全(主體在外,只公開 public)

app-laravel/   ← 公開根目錄之外
├─ .env  .git/  ← 無法到達
└─ public/ → 從 public_html 處 require
主體放在 docroot 之外,只公開 public/。僅此一點就讓 .env 無法到達。

依你的環境選擇對策

「不讓 .env 被公開」的具體做法,會因伺服器類型和你的權限而不同。先找到符合你的那一列,再照下面的步驟進行。

環境永久修復緊急處置注意事項
共用虛擬主機(可用 SSH)把應用程式主體移出公開資料夾,只在其中留一個小的 index.php(Step 2)在 .htaccess 拒絕存取把公開資料夾本身改成 symlink,在某些環境會回傳 500
共用虛擬主機(只有 FTP)用 FTP 用戶端把應用程式主體重新上傳到公開資料夾(public_html 等)的上一層在 .htaccess 拒絕存取大多數共用主機允許寫入公開資料夾的上一層;如果不行,請詢問客服
控制台可以設定公開資料夾把公開資料夾直接指向 app-laravel/public不需要(仍要檢查)切換後,刪除留在舊公開資料夾裡的應用程式副本
VPS(Apache)把 VirtualHost 的 DocumentRoot 指向 public/在伺服器設定裡加入拒絕規則AllowOverride None(Apache 2.4 的預設值)時不會讀取 .htaccess
VPS(Nginx)把 root 指向 public/,並拒絕以點開頭的檔案在伺服器設定裡加入拒絕規則Nginx 不讀 .htaccess

無論哪種環境,目標都一樣:公開資料夾裡沒有 .env、.git、vendor/ 或 storage/。Laravel 官方文件也警告,把 index.php 移到專案根目錄並從那裡提供服務,會讓許多敏感的設定檔暴露在公開網路上。

Step 1:緊急處置(快速、可復原)

在公開根目錄的 .htaccess 開頭加上一段拒絕存取的規則(防禦用的設定範例)。不影響正常頁面。

# === SECURITY BLOCK ===
RedirectMatch 404 (?i)/\.(env|git)(\..*)?(/|$)
 
<FilesMatch "(?i)\.(sql|sql\.gz|bak|old|swp|save|orig|tgz)$|^credentials\.json$|\.bk[._]">
    Require all denied
</FilesMatch>
 
<FilesMatch "(?i)^(phpinfo|info)\.php$">
    Require all denied
</FilesMatch>
 
# if Laravel internals are visible at /<app>/storage/... etc.
RedirectMatch 403 (?i)^/[^/]+/(storage|bootstrap/cache|config|database|resources|routes|tests|vendor)(/|$)
# === END ===

緊急處置不是終點

黑名單總會漏掉什麼,例如 composer.lock 或任何新的檔名。把它當作撐到 Step 2 之前的權宜之計。

Step 2:結構改造(永久修復)

把應用程式主體移到公開根目錄之外,只留下一個負責載入它的小入口。

# 1. Move the body outside the web root (same filesystem → mv is instant, atomic)
mv ~/example/public_html/app  ~/example/app-laravel
 
# 2. Put only a minimal entry in the web root
mkdir -p ~/example/public_html/sub
cat > ~/example/public_html/sub/index.php <<'PHP'
<?php require __DIR__ . '/../../app-laravel/public/index.php';
PHP
 
# 3. Copy Laravel public/.htaccess, symlink static assets
# 4. Clear config cache
rm -f ~/example/app-laravel/bootstrap/cache/{config,services,packages,routes-v7}.php

本站維運得到的教訓:bootstrap-redirect 比 symlink 更穩

把 public_html 本身做成指向 public/ 的 symlink,在某些環境裡對已註冊子網域的 docroot 會回傳 500(不會跟隨)。讓 docroot 保持為真實目錄、由其中一個小小的 index.php 用絕對路徑 require(bootstrap-redirect 的做法),在權限範圍受限的共用主機上更穩定。此外,opcache 可能記住一次失敗的載入並持續回傳 500,最後的手段是更改入口檔名(entry.php)。

Step 3:輪換金鑰(以已被看到為前提)

只要存在過公開期間,就把 .env 裡的金鑰當作已被看到並替換掉。順序:外部 API/OAuth 金鑰 → 加密金鑰 → 郵件 → DB。也請參閱 .env 詞條。重新簽發 OAuth 金鑰可能使 refresh_token 失效,所以先準備好重新授權的流程。

Step 4:自我檢查(養成習慣)

最後,確認自己的網站實際上沒有公開任何東西(只針對自己擁有的網域)。

# A 200 with a body means it's exposed. 403/404 is okay for now.
curl -sI https://your-domain/.env | head -1
curl -sI https://your-domain/.git/config | head -1

每次部署都檢查一遍,就能及早發現目錄配置的失誤。

如何檢查自己的環境(更詳細)

除了 Step 4 的兩行指令,下面這些檢查能抓到更多常被遺漏的地方。只對自己的伺服器和自己的網域執行。

1

看內容,而不只是狀態碼

有些應用程式對任何未知的 URL 都回傳首頁和 200,所以只看狀態碼看不出什麼。只印出開頭部分,例如 curl -s https://your-domain/.env | head -c 120,看看有沒有出現 APP_KEY= 或 DB_PASSWORD= 這樣的設定行。對 .git/HEAD 來說,如果出現以 ref: refs/heads/ 開頭的一行,就表示已經公開。

2

每個應用程式都要檢查

不只主網域,子網域或子目錄上的每個應用程式都要重複檢查。新增應用程式時,配置上的失誤往往會被複製過去,修好一個不代表其他的也修好了。

3

在伺服器上搜尋公開資料夾

能用 SSH 的話,直接找出有風險的檔案,例如 find ~/public_html -maxdepth 4 \( -name ".env*" -o -name ".git" -o -name "*.sql" -o -name "*.zip" \)。沒有任何輸出才是理想的結果。只有 FTP 的話,在 FTP 用戶端打開「顯示隱藏檔案」,檢查檔案清單。

4

確認 .htaccess 真的生效

在公開資料夾放一個不含任何機密的無害測試檔(例如內容只有 test 的 check.bak),然後用 URL 打開它。應該得到 403。如果能打開,表示 .htaccess 沒有被讀取,請確認伺服器類型(例如 Nginx)或其設定。檢查完後刪除測試檔。

5

確認除錯模式已關閉

在 .env 裡,APP_ENV 應為 production,APP_DEBUG 應為 false。Laravel 文件指出,在正式環境設定 APP_DEBUG=true,有可能讓使用者看到敏感的設定值。能用 SSH 的話,php artisan about --only=environment 也能顯示相同資訊。

常見錯誤與修正

  • 刪了 .env 卻留下 .git:公開的 .git 可能讓人重建過去的 commit,包括舊的 .env 或金鑰。把 .git 也移出公開資料夾,並重新簽發任何曾被 commit 過的機密。
  • 在公開資料夾留下 .env.bak 或 .env.old 之類的副本:含有真實值的副本和 .env 一樣敏感。移出或刪除。.env.example 裡只放佔位值。
  • 把整個專案打包成 ZIP 上傳,解壓後沒刪:ZIP 裡含有 .env。解壓後立刻刪除。
  • 以 APP_DEBUG=true 運行正式環境:錯誤頁面可能洩漏設定值。正式環境一律設為 false。
  • 以為寫了 .htaccess 就安全:Nginx 和 AllowOverride None 的環境根本不讀它。用上面的測試檔方法確認。
  • 突然更換 APP_KEY:依 Laravel 文件,更換金鑰會讓所有已登入的使用者被登出,用舊金鑰加密的資料也無法解密。Laravel 11 以後的版本,切換前先把舊金鑰放進 APP_PREVIOUS_KEYS。

實際案例:公開的 .env 被大規模蒐集

2024 年 8 月,一家資安公司的研究團隊報告了一起攻擊行動:自動蒐集網頁伺服器上公開的 .env 檔案,利用其中的雲端憑證把資料複製出去,再勒索贖金。根據報告,約 110,000 個網域成為目標,蒐集到超過 90,000 個環境變數,其中約 7,000 個是雲端服務的憑證,約 1,500 個是社群媒體帳號的憑證。

報告認為原因不在雲端服務本身的漏洞,而是受害組織的設定錯誤,誤將 .env 檔案公開。由此可以得到兩點教訓:

  • 公開的 .env 不是偶然被發現的,而是被程式大規模搜尋出來的
  • 一個 .env 就可能讓損害擴散到雲端儲存、電子郵件等其他服務

不要以為小網站不會被找到。同樣的配置失誤的匿名案例,請見 .env 全公開的故事。

接著閱讀

出處

  • Laravel 文件「Deployment」(讓請求指向 public/index.php、不要從專案根目錄提供服務、正式環境設 APP_DEBUG=false):laravel.com
  • Laravel 文件「Configuration」(不要把 .env commit 到版本控制;Debug Mode):laravel.com
  • Laravel 文件「Encryption」(輪換 APP_KEY 與 APP_PREVIOUS_KEYS):laravel.com
  • Apache HTTP Server 2.4「core - AllowOverride」(設為 None 時不讀 .htaccess;預設值為 None):httpd.apache.org
  • Apache HTTP Server 2.4「.htaccess files」(使用時機與注意事項):httpd.apache.org
  • OWASP「WSTG-CONF-04 Review Old Backup and Unreferenced Files for Sensitive Information」:owasp.org
  • Unit 42(Palo Alto Networks)「Leaked Environment Variables Allow Large-Scale Extortion Operation in Cloud Environments」(2024 年 8 月 15 日):unit42.paloaltonetworks.com

FAQ

Q現在馬上能做的對策是什麼?
A

在公開根目錄的 .htaccess 裡加上一段拒絕存取 .env、.git、SQL 傾印、credentials.json 等檔案的規則。它可以復原,也不影響正常頁面。但這只是應急處理,長久的修復是結構改造。

Q為什麼只靠 .htaccess 不夠?
A

黑名單每多一個新檔名就需要新的規則,總會有遺漏。只要把應用程式主體移到公開根目錄之外,.env 就根本無法到達,也不需要維護規則。

Q用 symlink 把 public/ 公開出去不行嗎?
A

在某些環境下,把已註冊子網域的 docroot 用 symlink 改指向另一個目錄樹時不會跟隨,會回傳 500。按本站的經驗,讓 docroot 保持為真實目錄、由其中一個小小的 index.php 用絕對路徑 `require`(bootstrap-redirect 的做法)更穩定。

Q怎麼確認自己網站的 .env 能不能從外部看到?
A

只針對自己擁有的網域,用 curl 或瀏覽器打開 /.env 和 /.git/HEAD,同時看 HTTP 狀態碼和內容。403 或 404 暫時沒問題。如果回傳 200 並顯示像「APP_KEY=」這樣的設定行,就是已經公開了。如果你在子網域或子目錄上運行多個應用程式,每一個都要檢查。能用 SSH 的話,也在伺服器上搜尋公開資料夾裡有沒有 .env 或 .git。

Q如果伺服器是 Nginx,或 .htaccess 不生效怎麼辦?
A

Nginx 不讀 .htaccess。在 Apache 上,AllowOverride 設為 None 時 .htaccess 會被忽略。這種情況下,要在伺服器設定裡把公開根目錄(root 或 DocumentRoot)指向 Laravel 的 public/。Laravel 官方文件裡的 Nginx 範例也包含一條拒絕以點開頭的檔案(.well-known 除外)的規則。

Q如果 .env 曾經被公開,刪掉檔案就夠了嗎?
A

不夠。如果有人在公開期間取得了它,裡面的金鑰在檔案刪除後依然有效。要重新簽發 API 金鑰、OAuth 金鑰、DB 與郵件密碼以及 APP_KEY,並在各服務的活動紀錄裡檢查有沒有不認得的操作。如果 .git 也曾被公開,就把過去任何一次 commit 裡出現過的機密都當作已外洩。