WordPress 網站被駭清理乾淨之後又復發,並不是特例。根據 Sucuri 在 2023 年度發布的〈Hacked Website & Malware Threat Report〉,網站清理當下仍有 49.21% 的案例至少殘留一個後門,換句話說,將近一半自認已經清乾淨的網站,其實根本沒有清乾淨。
如果清理過一輪之後又在瀏覽器跳出安全警示,或是同一個轉址、同一支陌生檔案又出現在同一個位置,這種復發代表的不是運氣不好,而是原本的清理沒有真正碰到讓它反覆重生的那個環節。網站被標成不安全,流量會在短時間內明顯下滑,排名也可能連帶受影響,讀者與客戶對這個網域的信任同樣會打折扣,反覆清理只會讓這些損失一次一次重演。
真正決定復發還是止血的,往往是三個最常被忽略的主機層病灶,不是外掛沒更新,也不是密碼不夠複雜,而是藏在主機帳號、甚至伺服器層裡的問題。某次教科書等級的清理,卻在重新上線幾分鐘內又中,就是最典型的例子。

重灌 WordPress 沒能擋住幾分鐘內的復發
某個 WordPress 網站被瀏覽器標成不安全之後,管理者決定做一次徹底的清理,整個網站程式碼重新安裝,資料庫也建了全新的一份,wp-config.php 裡的所有金鑰重新產生,連 cPanel、FTP、資料庫的密碼都一併換過。這已經是坊間「WordPress 被駭怎麼辦」教學會建議的完整流程,照理不該再留下任何漏洞。網站重新上線沒幾分鐘,瀏覽器又跳出同樣的安全警示,惡意轉址與陌生的後門檔案,再次出現在同一個位置。
這個結果推翻了一個很常見的假設,只要把 WordPress 這個應用程式清乾淨,問題就解決了。當應用層的清理已經做到最徹底,不是漏了幾個外掛沒重灌,也不是資料庫忘了換新,復發代表病灶根本不在 WordPress 這一層,而是藏在主機帳號層,甚至更底下的伺服器層。常見的情況有三種:cPanel 的排程工作把清掉的後門重新種回去、同一台主機上其他網站的感染橫向擴散過來,或是後門本身就藏在核心資料夾以外、重灌核心根本不會動到的角落。多數「WordPress 被駭處理教學」不會提到這三種情況,因為那些教學談的是網站程式碼與資料庫層級的防護,處理的是應用層問題,不是主機帳號的鑑識調查。而 WordPress 官方自己的硬化指南,其實老早就寫明了這個邊界。

官方硬化指南從不保證主機層是乾淨的
WordPress 官方的進階管理文件《Hardening WordPress》裡有一段常被忽略的但書。文件寫明,如果使用的是與其他網站共用的主機環境,只要同一台主機上有其他網站被入侵,即使把這份指南列出的每一項都做到,網站仍然可能因此被牽連。這句話把讀者從「是不是哪個步驟沒做對」的自我懷疑,導向另一個事實,這件事本來就在官方文件裡被承認是這份指南顧不到的範圍。
這份指南實際涵蓋的項目,全部落在網站程式碼與資料庫層級。檔案權限方面,建議根目錄、wp-admin、wp-includes 只能由網站擁有者的帳號寫入,wp-content 才開放給執行網站的伺服器行程寫入;wp-config.php 可以搬到網站根目錄外一層並設成 400 或 440 這種更嚴格的權限;後台內建的主題與外掛編輯器建議直接停用,作法是在設定裡加上 DISALLOW_FILE_EDIT;多個網站共用同一台主機時,建議每個網站各自使用不同的資料庫帳號,這樣一個網站被攻破,不會連帶波及其他網站;再加上定期備份、並保留檔案的雜湊值做完整性比對。
這些項目全部屬於網站程式碼與資料庫層級的防護,完全沒有涵蓋作業系統層的排程工作,也沒有處理同一台主機上不同帳號之間的隔離設定。指南顧不到的那一塊,往往就藏在主機的排程工作裡。
排程工作,把清掉的後門在幾分鐘內種回來
排程工作(cron job)是 Linux 主機環境用來定期自動執行指令的機制,本身是正常功能,備份、清快取、寄送報表都靠它運作。攻擊者拿它來當重建點,清乾淨檔案之後,排程工作在下一次執行時,可能每 15 分鐘,也可能每一分鐘,就重新把後門寫回去。這正是「明明清乾淨了,過沒多久又中」最直接的技術原因,也是為什麼單純還原備份沒有用。備份還原只換掉檔案與資料庫,完全不會動到主機作業系統層的排程表,只要那個排程工作還在,剛還原乾淨的網站,下一次排程執行就會被重新種回後門。
Sucuri 在一篇分析文章中說明,惡意排程工作常見的手法是定時用 wget 或 curl 從外部網址下載惡意內容並立即執行,執行完再把下載下來的檔案刪除,讓惡意行為不留在磁碟上被掃描工具抓到。文中也記錄過攻擊者把整段經過 base64 編碼、大小約 30KB 的後門或 webshell 內容直接寫進排程指令本身,每次執行時只要檢查到目標檔案是空的或不存在,就重新解碼寫回去,並設成唯讀權限,防止被輕易修改。
要抓出這類排程,第一步是打開 cPanel 後台的「Cron Jobs」頁面,或 Plesk 的「Scheduled Tasks」頁面查看,有 SSH 權限的話,可以直接用 crontab -l 指令查看目前帳號底下的排程內容。清理的順序也很重要,一定要先確認排程工作已經清除,才動手清理檔案,順序顛倒的話,排程會一邊執行、一邊把剛清掉的檔案重新寫回去。
cPanel 排程列表看不到的其他帳號
cPanel 後台的「Cron Jobs」頁面與 Plesk 的「Scheduled Tasks」頁面,只會顯示登入帳號自己的排程表。這是因為 Linux 底下每個使用者帳號各自擁有獨立的排程表,帳號層級的頁面本來就看不到其他帳號的內容。如果只是打開這個頁面看一眼,發現是空的就以為排程沒問題,其實只驗證了自己這個帳號是乾淨的,完全沒有驗證伺服器上其他帳號、或作業系統層更高權限的排程是不是乾淨。這正是「明明查過了怎麼還會這樣」最常見的誤判來源。
帳號層級能讀寫的排程檔,實際位置是 /var/spool/cron/USERNAME(cPanel 與 RHEL 系統常見)或 /var/spool/cron/crontabs/USERNAME(Debian、Ubuntu,以及多數 Plesk 環境)。root 層級的排程則完全不會出現在任何主機控制台介面裡,只有擁有主機管理權限的人才看得到、改得到,位置包括 /etc/crontab 這份主排程表、/etc/cron.d/ 這個獨立排程片段目錄,以及 /etc/cron.hourly、/etc/cron.daily、/etc/cron.weekly、/etc/cron.monthly 這幾個放整支腳本、以 root 權限執行的資料夾。mySites.guru 一篇技術文章記錄過一個實際案例,攻擊者把檔案放在 /etc/cron.daily/cpanel_sync 這個位置,檔名刻意模仿 cPanel 自己的合法排程檔案,內容是一行用十六進位編碼過的網址、透過 curl 下載後直接交給 bash 執行,每天以 root 權限跑一次,會把一支後門檔案重新寫回主機上每一個帳號、每一個網站的每個資料夾,同時把主機資訊回報給攻擊者。文中指出,在這個 root 層級的排程被移除之前,清理網站本身沒有意義,因為伺服器上每一個網站,下一次排程執行時都會被重新感染一次。
藏在資料庫裡的 WordPress 內建排程
除了作業系統層的排程,WordPress 自己也內建一套排程系統,叫做 WP-Cron,跟前面講的作業系統層 cron job 是完全不同的兩套機制。WP-Cron 排定的事件存放在資料庫 wp_options 資料表裡的 cron 這一筆選項,不是任何檔案,也不是作業系統的排程表。這代表就算主機管理員把伺服器上所有作業系統層的排程都查過一遍、確認完全乾淨,惡意程式仍然可能靠著登記一筆惡意的 WP-Cron 事件,在資料庫層級自行重新啟動。
mySites.guru 同一篇文章把這個分野講得很清楚,WordPress 有自己的排程系統 WP-Cron,事件存在資料庫的 cron 欄位裡,不在任何檔案或作業系統的排程表中,後門可以完全靠資料庫裡的這筆紀錄自我重啟,就算伺服器上每一份作業系統排程表都讀起來乾乾淨淨也一樣。真正完整的排查要涵蓋三層,作業系統的排程表(含所有 root 層級的位置)、CMS 自己的排程機制(WordPress 的 WP-Cron,或其他系統各自的排程資料表),以及伺服器層的 at 排程與 systemd timer,漏查任何一層,都可能讓復發的原因始終沒被抓到。查完排程之後,下一個容易被忽略的地方,是後門檔案本身藏在哪裡。
後門通常落腳在核心資料夾以外的角落
清理時最容易漏掉的第二個病灶,是後門檔案本身。多數人重灌時只重新安裝 wp-admin、wp-includes 這些核心目錄,卻忽略了後門最常被藏起來的地方,通常是不會被自動覆蓋、也很少有人去檢查的角落,包括沒在用的舊佈景主題、外掛資料夾、上傳目錄裡的圖片資料夾,甚至偽裝成核心檔案本身。就算重灌了 WordPress 核心,只要這些角落還留著一個後門,攻擊者就能重新取得存取權限,等於地基換了新的,地下室卻還留著一把備用鑰匙。
重灌核心跟清乾淨所有後門是兩件事。後門之所以偏好躲在核心目錄以外,邏輯很直接,這些地方不會因為 WordPress 更新而被自動覆蓋,也很少有人手動一一檢查。WPBeginner 一篇整理被駭網站後門排查方式的文章,完整拆解了下面四個最該檢查的角落。
沒人再打開的舊佈景主題與外掛,後門最愛留在這裡
佈景主題與外掛的檔案不會被 WordPress 核心更新覆蓋,尤其是已經停用、卻沒有刪除的舊佈景主題與外掛,多數人一年都不會打開檢查一次,是後門存活率最高的地方。WPBeginner 指出,佈景主題(尤其不是目前使用中的那個)裡的程式碼在更新 WordPress 時不會被覆蓋,是很好的後門藏匿處,外掛的情況也一樣,同樣不會被核心更新動到。
比起逐一翻找每個檔案,WPBeginner 建議的做法是直接刪除整個 themes 資料夾與 plugins 資料夾,再從官方管道,例如 WordPress.org 的佈景主題與外掛目錄,或是原本購買的管道,重新安裝需要用到的那幾個。這是唯一能確定這個資料夾裡不會再留下任何殘留的方式,逐檔翻找反而容易漏掉一支偽裝得很好的檔案。
上傳資料夾裡不該出現的 PHP 檔案
wp-content/uploads 資料夾的設計用途是存放媒體檔案,正常情況下裡面不會有任何 PHP 檔案。這正是它常被拿來藏後門的原因,多數人上傳圖片之後就不會再回頭檢查這個資料夾實際存放了什麼內容。實務上可以逐年、逐月的資料夾去檢查,也可以用指令一次找出藏在裡面的所有 PHP 檔案。
WPBeginner 舉了兩個真實抓到的例子。一支命名為 hello.php 的檔案被放在 uploads 資料夾裡,偽裝成 WordPress 內建的範例外掛 Hello Dolly,另一支放在 wp-includes 資料夾裡的檔案叫 wp-user.php,這個檔名在正常的 WordPress 安裝裡根本不存在。文中提供的檢查指令是 find uploads -name "*.php" -print,正常情況下這個指令應該回傳空結果,一旦列出任何檔案,就代表 uploads 資料夾裡混進了不該存在的程式檔。另外要留意的是,有些後門刻意不使用 .php 副檔名,改用像 wp-content.old.tmp 這種命名方式,或是直接偽裝成壓縮檔,用來規避單純比對副檔名的掃描方式。
WP-CLI 比對雜湊值揪出核心與外掛裡被動過手腳的檔案
比起肉眼逐檔比對,有一個更可靠的檢查方法,用 WordPress 官方的指令列工具 WP-CLI,直接比對目前安裝的核心檔案與外掛檔案,跟 WordPress.org 官方發布版本的雜湊值是否一致,只要不一致,就代表檔案內容被改過,或是多了一支官方版本裡不存在的檔案。
WordPress 官方 WP-CLI 文件說明,wp core verify-checksums 這道指令會下載目前安裝版本對應的官方 MD5 雜湊值,逐一比對已安裝的核心檔案,標出內容被改過的檔案,以及核心目錄裡多出來、官方版本裡不存在的檔案,這正是後門檔案常見的落腳方式。wp plugin verify-checksums --all 則對所有已安裝的外掛做同樣的比對,官方文件也明確註明,這道指令只能檢查存在於 WordPress.org 外掛目錄裡的外掛,從其他管道取得的付費外掛沒辦法用這個方式驗證,需要另外靠人工檢查,或是跟版本控制紀錄比對。
資料庫裡多出來的管理員帳號也是一種後門
後門不一定是一支檔案,也可能是資料庫裡一個看起來眼熟、其實不是自己建立的管理員帳號。攻擊者建立一個新的管理員帳號之後,就算把所有惡意檔案都清乾淨、密碼也全部換過,只要這個帳號還在,攻擊者一樣能直接用帳號密碼登入後台,不需要再靠任何後門檔案。清理完檔案之後,一定要回頭檢查使用者清單,確認裡面沒有自己不認得的管理員帳號。
Sucuri 在 2023 年度的〈Hacked Website & Malware Threat Report〉裡提供了具體數字。在所有被植入資料庫層惡意程式的網站當中,55.2% 存在至少一個惡意建立的管理員帳號,讓攻擊者能在清理後直接用帳密重新取得完整後台權限,不需要再依賴任何後門檔案。報告也統計出這些惡意帳號常見的使用者名稱與信箱格式,例如帶隨機字元的 wp_update-、wp-demouser-44、wp-import-user 這類命名模式,信箱則常見 [email protected]、[email protected] 這種泛用格式,可以拿來對照自己網站的使用者清單裡有沒有類似規律的可疑帳號。
同一份報告另外指出,49.21% 的受駭網站在清理當下至少存在一個後門,前一年 2022 年是 69.63%,2021 年是 60.04%,三年當中以 2023 年最低,但仍逼近半數,稱不上樂觀;10.89% 的受駭網站含有至少一種現成的攻擊工具組,這類工具能協助攻擊者更快取得存取權限、散布惡意程式、攻擊同主機的其他網站、發動 DDoS 攻擊,或在受駭環境裡執行管理層級的操作;39.1% 的受駭網站在感染當下,使用的 CMS 版本已經過期未更新,13.97% 存在至少一個已知有漏洞的外掛或佈景主題。這兩個數字可以對照著看,版本更新仍然是佔比最高的入口,但即使版本更新做到位,也不代表後門與惡意帳號會自動消失,這是兩件要分開處理的事。
同主機的其他網站也可能是感染回流的破口
第三個、也是最容易被忽略的病灶,出現在共用主機的情境裡。如果這個網站跟其他網站共用同一個主機帳號,虛擬主機常見的附加網域模式就是這種情況,或是跟其他客戶共用同一台伺服器,感染可能根本不是從自己的網站進來的,而是從同一台伺服器上另一個較少維護的網站橫向擴散過來。這種情況下,就算把自己的網站徹底清乾淨、也守住了所有已知的弱點,只要感染源頭那個網站沒清,或是伺服器層的隔離設定本身有缺陷,過一段時間又會被重新感染,而且這種破口光靠自己一個帳號的權限完全查不出來,必須靠主機商從伺服器層介入才處理得了。
這種現象在資安領域稱作跨網站污染,或是橫向移動,指的是攻擊者從一個較弱的入口拿到權限之後,利用同一台主機上帳號之間的共用設定,逐步擴散到其他原本沒有直接弱點的網站。判斷什麼時候該找主機商、而不是繼續自己清理,關鍵就在於能不能先確認感染源頭是不是在自己的帳號範圍內。
cPanel 附加網域共用的檔案權限
多數虛擬主機方案讓一個 cPanel 帳號可以掛載多個附加網域,方便管理者用同一組帳號管理好幾個網站。這個方便的代價是,這些網站預設會由同一個系統使用者與群組擁有,執行 PHP 的伺服器行程也是用同一個身分在跑,代表其中一個網站的檔案天生就對其他網站的檔案有讀寫權限。平常沒有問題,一旦其中一個網站被入侵,整組帳號底下的所有網站會同時暴露在風險裡。
Sucuri 的分析文章指出,同一個 cPanel 底下的網站,預設會共用同一組擁有者權限,一個網站的檔案因此天生就能存取、寫入另一個網站的檔案,反過來也一樣。平常這不是問題,一旦有惡意程式進到這個環境,很快就會從一個網站的問題,變成整組帳號底下所有網站的問題。文中也提到一個常見情境,管理者通常只把心力放在一兩個主力網站上,其他附加網域上順便架設的網站容易疏於更新,變成整組帳號最先被攻破的入口。
symlink 漏洞可以讓入侵者跨帳號拿到主機的 root 權限
還有一種更進階的跨帳號攻擊手法。即使兩個網站分屬不同的 cPanel 帳號,理論上應該互相隔離,只要主機沒有正確設定 symlink 保護,攻擊者仍然可能利用符號連結相關的技巧,跨帳號取得原本讀不到的檔案與權限。這不是紙上談兵的理論風險,CISA 在 2026 年 6 月將 LiteSpeed cPanel 外掛的一個 symlink 瑕疵(CVE-2026-54420)列入「已知遭利用漏洞」(KEV)名錄,官方說明指出,攻擊者只要先在共用主機上的某個帳號取得 FTP 或 web shell 權限,就能利用這個瑕疵繞過 CloudLinux CageFS 原本用來隔離不同帳號的機制,一路取得伺服器的 root 權限。這裡只講這個漏洞存在、影響是什麼,不描述具體的攻擊操作步驟。
在防護面,Sucuri 建議在主機的 Apache 全域設定裡啟用 symlink 保護,並搭配 PHP 的 open_basedir 限制,降低攻擊者跨帳號讀取檔案的可能性。對有能力配置環境的情況,VPS 環境改用 PHP-FPM,讓每個網站以各自獨立的系統使用者執行,會比所有網站共用同一個伺服器行程身分安全得多。
破口在別人的帳號時,自己一個人查不出來
如果前面查過自己網站的排程、後門檔案、管理員帳號都確認乾淨,卻還是持續復發,就該考慮感染源頭根本不在自己帳號裡的可能性。一般網站管理者的權限範圍查不到其他帳號,也看不到伺服器層的排程與程序,唯一能做的是把已經查到的證據,例如清理的時間點、復發的時間點、找到的惡意檔案樣本,整理清楚提供給主機商,請他們從伺服器層檢查是不是有跨帳號的異常活動,或是其他受感染的客戶帳號。
Sucuri 與 mySites.guru 兩篇文章的結論方向一致,跨帳號污染的判斷與清除,本質上超出一般網站擁有者的帳號權限範圍,需要主機代管商,或是 VPS 環境裡有伺服器管理權限的人,在伺服器層級介入才查得到、也才清得掉。這跟 root 層級後門的邏輯一樣,在有 root 權限的人把那個排程移除之前,網站擁有者自己清網站本身沒有意義。
主機商願不願意查到根層級,決定接下來的收尾方式
前面拆出來的三個病灶,排程工作、藏在核心資料夾外的後門、同主機其他帳號的交叉感染,最後可以收斂成一個判斷。問題如果真的落在主機帳號層或伺服器層,網站擁有者自己的權限修不到,這時候能不能真正解決,取決於主機商願不願意、有沒有能力配合查到根層級。
請主機商協助時,提供的資訊愈具體,對方願意深入查的機率愈高。實務上該準備的內容包括清理的確切時間點、復發出現的時間點、找到的惡意檔案或排程樣本,以及懷疑是同主機交叉感染的具體理由,例如檢查過自己帳號的排程與檔案都乾淨、卻仍然重複中招。如果主機商查不出來、不願意配合,或是主機方案本身的架構就沒有帳號隔離能力,例如低價共享主機把大量帳號擠在同一組系統使用者底下,搬到有落實帳號隔離的主機,每個網站有各自獨立的系統使用者、獨立的資源限制,是更務實的長期做法,而不是繼續在原地重複清乾淨、又復發、再清一次的循環。

止血的前提是查到根層級的排程後門
止血的判準,跟排程工作背後的技術邏輯是同一回事。只要伺服器上任何一個角落,不管是自己的帳號、別人的帳號、還是 root 層級,還留著一個會重新寫回惡意程式的排程工作,這場清理就還沒有真正結束,只是清理與復發的循環又跑了一輪。
mySites.guru 文章提出的清理順序原則同樣適用在這裡,先處理排程或根層級的問題,再清網站檔案,順序顛倒的話,檔案每清一次就被重新感染一次,等於白費工夫。真正止血的判準是排程工作已經確認移除,不是這次看起來乾淨了。
主機商不配合時,隔離式主機是務實的下一步
如果主機商查不出來,或是主機方案本身的架構就沒有做帳號隔離,與其在原地重複清理循環,搬到有落實帳號隔離的主機環境,是止住反覆復發更務實的做法。這裡指的帳號隔離,包括每個網站有各自獨立的系統使用者、主機端有做 symlink 保護,甚至用上像 CageFS 這類容器隔離技術,把不同帳號的執行環境完全分開。
這不是逃避問題,而是承認原本的主機環境本身就是這個問題的一部分。Sucuri 對怎樣的主機環境算是有做好隔離,給出的判準包括獨立的系統使用者、symlink 保護,以及 PHP-FPM 逐站隔離這幾項,可以直接拿來當作評估新主機時的檢查項目。
洗白後的觀察期與警示解除的判斷
排程與跨帳號的問題都處理掉之後,接下來還有一件事容易被忽略。很多人以為前台看起來正常、瀏覽器的安全警告消失,就代表整件事已經結束。但如果復發的原因本來就是間隔性的排程工作,例如一天才執行一次的 /etc/cron.daily,看起來乾淨的假象很可能只是還沒到下一次執行的時間點。
Google 官方的支援文件對這個流程有明確建議,只有在整個網站確認完全清理乾淨之後,才應該提出安全性問題的審查請求,審查作業可能需要數天到數週不等。一份有效的審查請求需要做到三件事,說明網站當初實際出現的問題是什麼、描述已經採取了哪些修復步驟,並記錄修復後的實際結果。Google 也強調,如果沒有先找出並修補讓網站被入侵的根本弱點,網站很可能會再次被標記為不安全;只把表面症狀清掉、移除轉址、刪除可疑檔案,卻沒有處理造成復發的主機層根因,警示解除之後還是可能再次出現。
排程工作的執行頻率可以是每分鐘,也可以是每天、每週,甚至每月才跑一次。觀察期如果只抓清完當下到隔天這麼短,很可能還沒撐到下一次低頻率的排程執行,就誤判成已經解決。比較務實的做法是,確認排程工作已經逐一移除,或是主機商已經確認伺服器層乾淨之後,再抓一段涵蓋常見排程週期的觀察期,期間持續留意網站是不是重新出現轉址、陌生檔案或效能異常,確認都沒有再發生,再提出 Google 的審查請求。
把一個 WordPress 網站被駭之後救回來,靠的不是重灌了幾次、換了幾次密碼,而是有沒有真正查到讓它一再復發的那個環節。多數時候,答案不在 WordPress 的程式碼本身,而在主機帳號怎麼被規劃、排程工作有沒有真的清乾淨、伺服器層有沒有做好帳號之間的隔離。這幾件事,大半超出網站管理者一個人能查、能修的範圍,需要主機商在伺服器層介入才查得到根源。下一次評估主機方案的時候,帳號隔離做得好不好,值得跟空間、頻寬放在同一個層級認真比較,它決定的是網站一旦被駭之後,這次清理能不能真正清乾淨,而不是幾天後又從頭來一次。
