跳至內容
伺服器與 .htaccess

真正有用的 WordPress .htaccess 安全規則

市面上號稱 WordPress .htaccess 安全的內容多半只是灌水。真正能改變你攻擊面的規則其實很短——本文告訴你哪些是哪些

發布於

市面上號稱「WordPress .htaccess 安全」的內容多半只是灌水。真正能改變你攻擊面的規則其實很短:封鎖 wp-content/uploads 內的 PHP 執行、拒絕對 wp-config.php 的直接存取,以及關閉目錄索引。至於那些廣為流傳的做法——封鎖 xmlrpc.php「以阻擋暴力破解」、隱藏你的 WordPress 版本、貼上一份 200 行的惡意機器人 user-agent 清單——效果從微乎其微到純粹作秀都有。以下就說明哪些是哪些,以及原因。

先釐清一件事:.htaccess 只在 Apache(以及同樣會讀取它的 LiteSpeed)上才有作用。在 nginx 上它會被完全忽略——檔案就擺在那裡,而你卻以為自己受到保護。如果你的主機跑的是 nginx,這一切都不適用,你需要改用 serverlocation 區塊。動手編輯一個伺服器根本不會讀取的檔案、白白花上一小時之前,先用 curl -I https://yoursite.com 檢查一下 Server: 標頭。

真正重要的規則:/uploads 裡不准有 PHP

這一條才是值得做的。wp-content/uploads 在設計上就是全域可寫入的——每一次媒體上傳、每一個會儲存檔案的外掛,都會寫入這裡。如果攻擊者把一個 .php 檔案塞進這個目錄(透過有漏洞的上傳處理程式、一個不驗證 MIME 類型的圖片欄位、或一個被入侵的外掛),那麼是「小麻煩」還是「完整的遠端程式碼執行入侵」,差別就在於伺服器是否會在收到請求時執行那個檔案。拒絕執行,上傳的惡意程式就只是躺在磁碟上的一個惰性檔案而已。

把這段放進 wp-content/uploads/.htaccess(如果檔案不存在就建立它):

<FilesMatch "\.php$">
    Require all denied
</FilesMatch>

這是 Apache 2.4 的語法。在較舊的 2.2 上,對應的寫法是 Order Deny,AllowDeny from all。在同一個檔案裡混用這兩種語法,是「強化」之後突然出現 500 Internal Server Error 最常見的原因——如果你一存檔整個網站就掛掉,幾乎肯定就是這個問題。用 apachectl -v 檢查你的版本。

保護 wp-config.php

wp-config.php 存放著你的資料庫憑證與驗證 salt。當 PHP 正常運作時,直接請求這個檔案會回傳一個空白頁面——PHP 執行了這個檔案,而不是把它印出來。風險在於失效的情況:只要 PHP 曾經當掉、在搬遷過程中被設定錯誤、或處理程式被停用,Apache 就會把這個檔案當成純文字送出,把你的資料庫密碼傾倒給任何開口索取的人。拒絕存取,是為那糟糕的五分鐘買下的廉價保險:

<Files wp-config.php>
    Require all denied
</Files>

關閉目錄索引

如果有人造訪一個沒有 index.php 的資料夾,而 Apache 開著 Options +Indexes,它就會列出裡面的內容——每一個備份檔、每一份你早已忘記的零散 SQL 傾印。全站關掉它:

Options -Indexes

嚴重性不高,但確實存在,而且不花你任何成本。

這就是誠實的核心清單。你可以用 WordPress .htaccess 產生器把這些規則組合起來——連同正確的 2.4 對比 2.2 語法,讓你不會把網站搞出 500——而不必從一篇為錯誤 Apache 版本寫的論壇貼文裡複製貼上。

不該做的事

封鎖 xmlrpc.php「做暴力破解防護」。 這是人人跟著複製、卻講錯了的大重點。沒錯,XML-RPC 的 system.multicall 方法在歷史上確實讓攻擊者能把大量登入猜測綁進單一請求——這是真正的放大效果。但壓倒性最常見的暴力破解途徑,是對 wp-login.php 發出的普通 POST 請求,而封鎖 xmlrpc.php 對此毫無幫助。暴力破解要靠速率限制、強密碼和 2FA 來擊敗——而不是靠關掉一個端點。停用 XML-RPC 確實有正當理由:它的 pingback 功能可能被濫用來做 DDoS 反射攻擊,所以如果你沒在用 Jetpack、行動 App 或 pingback,關掉它能縮小你的攻擊面。只是別騙自己說這是你的暴力破解防線,因為它不是。

隱藏你的 WordPress 版本/移除 generator 標籤。 拿掉 readme.html<meta name="generator"> 標籤感覺像是在強化。但攻擊者能在幾秒內從排入佇列的資源查詢字串、區塊編輯器標記,以及其他十來種蛛絲馬跡,指認出你的版本。你什麼都沒藏到;你只是讓自己覺得很忙。

龐大的惡意機器人 user-agent 與 referrer 封鎖清單。 User agent 只是一個可以偽造的 HTTP 標頭。這些清單在你貼上去的那天就已經過時,它們攔不住任何有本事的對手,而 Apache 會對每一個請求評估每一條正規表示式——你付出了實實在在的效能代價,換來零安全性。跳過它們。

用 IP 鎖定 wp-login.php 很棒,直到你的 ISP 換掉你的位址,然後你把自己鎖在自家後台之外。只有在你有一個真正固定的 IP 時才可行。

作者列舉重新導向(?author=1)。 大家為此貼上的 .htaccess 重寫規則本身並不完整——REST 端點 /wp-json/wp/v2/users 照樣會列出使用者名稱。封住一條路徑、卻讓另一條敞開,這是作秀。

還是卡住了?

如果某條規則把網站搞成 500,那就是語法問題——把你最後加進去的區塊拉掉再重新載入;這樣能立刻把它隔離出來。如果某條規則看起來什麼都沒做,先確認你真的是在 Apache 上,並且該目錄的 AllowOverride 有被啟用(許多代管主機會限制它)。用 .htaccess 產生器從一份已知可用的範本建立檔案,留下那三條真正重要的規則,其餘的都捨棄。