跳至主要內容
>_ITDITD網站資安平台

資安指南

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

把 Laravel 等應用程式放到共用虛擬主機時,讓 .env、.git、vendor 無法被外部讀取的實戰指南。核心做法是『把應用程式主體放在公開根目錄之外,只對外暴露 public/』結構。本文圖解目錄配置的正誤,給出立刻能做的 .htaccess 止血、長期對策的步驟、本站在實戰中踩過的坑(bootstrap-redirect 比 symlink 更穩),以及自我檢查的方法。

發布於 2026-06-07 更新於 2026-06-07 閱讀時間 2 分鐘

對象:在共用虛擬主機(可使用 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 無法到達。

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>
 
# /<app>/storage 等內部目錄被暴露出來時
RedirectMatch 403 (?i)^/[^/]+/(storage|bootstrap/cache|config|database|resources|routes|tests|vendor)(/|$)
# === END ===

止血不是終點

黑名單一定會出現 composer.lock.gitignore 之類『忘了堵』的口子,是一場打地鼠。只能把它當作撐到 Step 2 之前的爭取時間手段。

Step 2:結構改造(長期對策)

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

# 1. 把主體移到公開根目錄之外(同一檔案系統時 mv 是瞬間完成且 atomic)
mv ~/example/public_html/app  ~/example/app-laravel
 
# 2. 在公開根目錄裡只建一個最小入口
mkdir -p ~/example/public_html/sub
cat > ~/example/public_html/sub/index.php <<'PHP'
<?php require __DIR__ . '/../../app-laravel/public/index.php';
PHP
 
# 3. 複製 Laravel public/.htaccess,把靜態資源用 symlink 連結
# 4. 清除設定快取
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:自我檢查(養成習慣)

最後,確認自己的站點上是否真的發生了外洩(只針對自己擁有的網域)。

# 回傳 200 且帶正文就說明被公開了。回傳 403/404 暫時算 OK
curl -sI https://你的網域/.env | head -1
curl -sI https://你的網域/.git/config | head -1

養成每次發布都檢查一遍的習慣,就能盡早發現目錄配置上的失誤。

接著閱讀

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 型』更穩定。