主機商寄來一封信,說你的網站這幾天不斷對外發送垃圾郵件。你打開首頁看起來完全正常,半信半疑跑了一次安全外掛的完整掃描,報告最後顯示乾淨,可是流量數字還是異常,垃圾郵件也還在源源不絕地寄出去。
這種掃描說乾淨、異狀還在的落差,其實指向同一件事情。WordPress惡意軟體掃描不是一次動作就能定案的檢查,查得到什麼,取決於你用哪一種方法去查。國際資安平台 MalCare 根據長年處理入侵網站的經驗指出,95% 的入侵源自外掛與佈景主題本身的安全漏洞,WordPress 核心本身反而極少是破口,多數網站中毒的起點,從一開始就不在多數人以為的地方。
讀完這篇,你會弄清楚不同掃描做法各自查得到什麼、又天生查不到什麼,之後不管是選一種,還是搭配著用,心裡都有個判斷依據,而不是裝了一套工具就以為高枕無憂。要弄懂為什麼掃描結果會跟實際狀況對不上,得先弄清楚惡意軟體平常都藏在網站的哪幾個位置。
惡意軟體在 WordPress 網站裡藏匿的三個層次
很多人以為掃描是一次動作,結果不是乾淨就是有問題,兩者只能二選一。但惡意軟體實際藏身的位置分成三個完全不同的層次,不同的掃描方法天生只碰得到其中一部分,漏掉的那部分,不會因為報告寫著乾淨就不存在。

第一層是檔案層,也是最容易被想到的一種。核心程式、外掛與佈景主題的檔案本身被植入或竄改,網站可能多出一支陌生的 PHP 檔案,或是原本合法的檔案裡被塞進一段惡意程式碼。
第二層是資料庫層,行為完全不一樣。駭客不需要動任何一個檔案,只要把惡意連結、垃圾廣告或跳轉腳本寫進文章內容、選項表(wp_options)這類資料庫欄位,網站前台一樣會出現異狀,檔案本身卻找不到任何被改動的痕跡。MalCare 也直接點出,資料庫同樣是入侵者的攻擊目標,常常被塞滿惡意內容,只檢查檔案的掃描方式並不完整。
第三層是最難纏的執行後自我清除型。這類惡意程式的邏輯是先執行一次任務,例如寄送垃圾郵件、把使用者帳密外傳,或是把惡意內容寫進資料庫,任務做完就自己刪除自身的檔案。資安公司 Snicco 與網站資安監控清除服務 WeWatchYourWebsite、WordPress 伺服器管理平台 GridPane 合作,並找資安公司 Patchstack 擔任第三方驗證、2023 年發表的一份研究,說明了這類程式的結構通常是一段簡單的程式碼,在執行完惡意動作之後,靠一行 @unlink(__FILE__) 這樣的指令,就能讓自己從硬碟上消失。掃描要抓到這種惡意軟體,得剛好在它執行的那一毫秒同時掃描,實務上幾乎不可能。
至於為什麼中毒的起點多半落在外掛與佈景主題,而不是 WordPress 核心本身,道理也不難懂。核心程式碼由官方團隊維護、更新頻繁,又被全世界一起盯著,漏洞很快就會被抓出來補上;外掛與佈景主題卻是由成千上萬個不同的開發者各自維護,品質參差不齊,有的甚至已經多年沒更新,自然容易被找到破口。依 MalCare 的經驗估計,弱密碼、登入攻擊、社交工程這類帳密外洩管道加起來不到 5%,主機本身的安全問題更是不到 1%,兩者合計都遠遠比不上外掛與佈景主題漏洞造成的比例。
線上掃描工具的檢查範圍止於公開可見的頁面
第一種能實際動手做的方法,是線上掃描工具。這類服務不用登入網站後台,也不用安裝任何外掛,只要把網址貼上去就能查,對剛才提到的懷疑最省事。它能查到什麼、查不到什麼,關鍵不在工具做得好不好,而在它天生的運作方式。整個檢查靠的是像瀏覽器一樣去瀏覽網站對外公開的頁面,看得到的只有訪客能看到的那個畫面。
SiteCheck 掃描報告的檢查項目與判讀方式
以 Sucuri 提供的 SiteCheck 為例,操作只有兩步:把要檢查的網址貼進輸入框,按下送出,系統就會對這個網域的公開頁面跑一次遠端掃描。整個過程不用註冊帳號,也不需要提供網站的任何存取權限,幾十秒內就會拿到一份報告。
報告通常會列出幾類明顯的問題,像是頁面裡藏的惡意連結、被植入的可疑 iframe、還有讓人看不懂在做什麼的可疑腳本,如果網站已經被主流廠商列進黑名單,報告也會直接標示出來。就算掃描沒有直接抓到惡意內容,Sucuri 也建議手動翻一次報告裡的 iFrames、Links、Scripts 這幾個分頁,自己找有沒有不熟悉或看起來可疑的元素,不要只看報告最上面那一行紅字或綠字就下結論。
如果同一台主機上掛了不只一個 WordPress 網站,Sucuri 的建議是每一個站都各自掃一次,不要只查出問題的那一個。同一主機下,一個網站中毒往往會透過共用的檔案權限或資料庫連線波及其他站台,這種跨站污染正是網站反覆中毒卻怎麼查都查不到源頭的常見原因。
登入後台、資料庫內容都在線上掃描的視野之外
線上掃描抓不到的東西,都來自同一個原因,它模擬的是訪客用瀏覽器看網站的視角,只能看到公開頁面實際渲染出來的畫面。凡是要登入才看得到的東西,像是後台的檔案總管、外掛清單裡有沒有多出陌生項目、資料庫裡實際存了什麼內容,線上掃描原則上完全碰不到。
另外還有一類問題,本身就不會反映在瀏覽器畫面上,像是被植入的後門程式、伺服器端才會執行的腳本,或是一個只有透過特定連結才會顯示、平常藏得好好的釣魚頁面。Sucuri 直接點出,有些問題只發生在伺服器端,不會顯示在瀏覽器裡,真正完整的做法是遠端掃描搭配伺服器端掃描一起做。MalCare 也提到類似的限制。線上掃描器雖然方便,卻因為沒有存取核心檔案與設定檔的權限,沒辦法掃描完整的網站;至於資料庫,線上掃描本來就完全碰不到。換句話說,「線上掃描顯示乾淨」這句話,充其量只代表訪客看得到的那個畫面暫時沒有異狀,不能當作「整個網站都沒事」的證明。

外掛掃描把資料庫和核心檔案都納入比對範圍
第二種能自己動手做的方法,是裝在網站上的安全外掛,或是把網站同步到廠商雲端去掃描。跟線上掃描工具比起來,這類做法有伺服器層級的存取權限,可以做到前面那種方法天生做不到的事:逐一比對核心與外掛檔案有沒有被改動、翻遍資料庫找可疑內容、還能排程讓它自動定期掃描,不用每次都手動觸發。
實際做法又分成兩種。一種是本地掃描,像 Wordfence 這樣,運算直接在自己的主機上跑;另一種是同步雲端掃描,像 MalCare 這樣,把網站的檔案與資料同步到廠商的伺服器上比對,不會佔用自己主機的資源。兩種各有取捨,本地掃描要吃掉主機的運算資源,掃描時網站可能會變慢;雲端掃描雖然不吃主機資源,卻代表你得把網站的檔案與資料庫內容傳到廠商的伺服器上,對資料隱私比較敏感的網站,這是要一起考慮的成本。
Wordfence 掃描結果先看風險分級
以 Wordfence 為例,實際操作是安裝外掛、啟用,然後到掃描頁面手動開始一次新掃描。Wordfence 平常會自動排程,免費版每天跑一次只做部分檢查的快速掃描、每 72 小時才跑一次完整掃描,懷疑網站出狀況時不必等排程,直接手動跑一次,才會照目前選定的掃描類型把核心、外掛、佈景主題的檔案都納進比對範圍。掃描跑完之後,Wordfence 會依嚴重程度把結果分成 Critical(嚴重)、High(高)、Medium(中)、Low(低)四級,Critical 要馬上檢查、High 要盡快處理,方便你先處理最要緊的項目。
其中特別要優先處理的是標示為「WordPress 核心裡出現不明檔案」這一類結果,因為核心的每一個檔案都對得上官方發佈的版本,多出一支陌生檔案,很可能就是被植入的惡意程式。不過看到警告不代表可以馬上按下全部刪除,Kinsta 提醒,被標記的檔案不一定真的是惡意軟體,有可能是網站正常運作需要、只是剛好跟官方版本不一樣的合法檔案,動手刪之前務必先看清楚警告內容,也要先備份好網站,免得刪錯東西反而把網站弄壞。
動態生成的程式碼與被竄改的掃描器,都是外掛看不到的死角
外掛掃描不是萬能的,這裡誠實講清楚它天生碰不到的地方,而且每一點都有研究實際示範或統計佐證,不是空泛的擔心。第一個死角是掃描器本身的運作環境。本地掃描器跟惡意程式一樣,都是在同一個 PHP 執行環境裡跑,理論上惡意程式可以反過來竄改掃描器本身,讓自己被放行。前面提到的那份 Snicco 研究就給出了實際數字,WeWatchYourWebsite 執行長 Thomas Raef 統計,過去 60 天內共有 52,848 個裝了 Wordfence、卻仍被入侵的網站,其中 14%(7,399 個)的案例裡,惡意程式確實動過 Wordfence 本身的檔案;同一份研究裡,換成 MalCare 與 VirusDie 的樣本,檔案被動過的比例則分別是 22% 與 24%。
第二個死角是動態生成的程式碼。多數掃描器靠比對程式碼裡的可疑關鍵字串,像 eval、base64_decode 這類函式名稱來抓可疑內容,但如果惡意程式改成用程式在執行的當下才拼出這些字串,比如把 var_dump 拆成好幾段字串,執行時才組合起來,靜態掃描就完全看不出程式碼裡藏著這個函式。這份研究實際示範過一段惡意程式,原本的寫法一放上去就被 Wordfence、NinjaScanner 標記出來,但只是改成一層最基本的混淆寫法,同樣兩套掃描器就完全偵測不到。
第三個死角,就是前面提過的執行後自我清除型惡意程式。這類程式的結構通常是先執行任務,寄送垃圾郵件、把使用者帳密外傳,或是把惡意內容寫進資料庫,做完就自己刪除自身檔案,掃描器只有剛好在它執行的那一毫秒同時掃描才抓得到,實務上等於抓不到。Snicco 的研究也指出,雲端掃描一樣是定期把網站當下的狀態傳回去比對,面對這種做完就消失的程式同樣束手無策,這是掃描機制本身的結構性限制。
手動比對核心檔案,靠的是官方校驗碼而不是特徵比對
第三種方法邏輯完全不一樣,是拿現在網站上的檔案,跟官方發佈的正版檔案逐一核對。前面兩種方法,不管是線上掃描還是外掛掃描,本質上都是在找可疑特徵,像是可疑字串、可疑的執行行為,而比對法要看的是雜湊值(checksum)對不對得上。
原理不複雜。官方對每一個版本的每一支檔案,都預先算好一組雜湊值公開提供,你只要把自己網站上的檔案重新算一次雜湊值,拿去跟官方那組比對,只要有一個位元組被動過,算出來的雜湊值就會不一樣,馬上就能抓出來。這個做法不依賴任何「像不像惡意軟體」的判斷,不管惡意程式碼寫得多隱密、多會拆解字串規避關鍵字比對,只要檔案內容跟官方版本有一絲差異,比對法都能發現。

用 WP-CLI 驗證核心與外掛檔案完整性
要跑這個方法,得先有終端機或 SSH 的存取權限,並且裝好 WP-CLI 這套指令列工具。Liquid Web 整理的做法是用 wp core verify-checksums 驗證核心檔案、用 wp plugin verify-checksums --all 驗證已安裝的外掛,指令會列出跟官方版本比對不上的檔案,先備份、確認過內容再處理,而且只有上架 WordPress.org 官方目錄的外掛才有官方校驗碼可比對。一般虛擬主機的網頁式後台多半沒有終端機,跑不了 WP-CLI,能用這個方法的通常是自己管理伺服器、或找得到工程師協助的網站,也因為門檻比較高,它通常不是日常掃描的預設工具,而是懷疑外掛掃描可能被繞過、想要更精確驗證時才動用。
沒有官方校驗碼時,改用異動時間與備份比對抓可疑對象
上一節留下一個缺口,付費外掛、客製主題、自己寫的功能外掛,官方都沒有公開校驗碼可以比對,wp plugin verify-checksums 這類指令對這些對象也起不了作用。遇到這種情況,邏輯得換一套,不再比對「跟官方版本像不像」,改成查「最近有沒有被動過」。
具體做法有兩種:一種是用指令列出最近一段時間內有異動的檔案清單,Sucuri 示範的做法是抓最近 90 天內修改過的檔案,天數也可以依需求縮短,抓出清單之後逐一打開檢視內容;另一種是拿現在的檔案跟一份確定乾淨的舊備份逐一比對差異,像 WinMerge 這類圖形化工具,或指令列的 diff,都能快速標出哪裡被動過,前提是手上剛好留著這樣一份備份。Sucuri 認為比對已知乾淨的備份是最可靠的做法,能直接指出差異所在,必要時甚至可以直接用備份還原,比重新清理更快。
不過發現差異,不代表一定是惡意軟體。網站的合法更新,或是自己動過的客製化,一樣會讓檔案跟備份不一樣。打開差異內容確認時,常常會看到 eval、base64_decode、gzinflate 這幾個函式,Sucuri 也提醒,這些函式外掛本身也會基於正當理由使用,看到不代表就是惡意軟體,需要人工判斷內容實際在做什麼,不能看到這幾個字就直接刪除,免得反而把網站弄壞。
三種方法的盲區,決定組合搭配的方式
把前面三種方法攤開來看,不是三選一的關係,而是每一種都補上另外兩種看不到的洞。線上掃描最快,不用裝任何東西,貼網址就能查,適合先做一次初步篩查,或是懷疑網站已經被主流廠商列入警示的時候,馬上查一次現況。外掛掃描補上資料庫與伺服器層級的監控,又能排程,適合當作日常的常態掃描,平常就掛著跑。手動比對核心檔案的雜湊值驗證最精確,適合懷疑外掛掃描可能被繞過,或是網站用了沒有官方校驗碼可以比對的客製外掛與主題,拿來當更深一層的驗證手段。
換句話說,線上掃描查得到的是公開頁面上看得見的異狀,外掛掃描在這之上多查到資料庫與伺服器層級的可疑內容,手動比對核心檔案再往下補上連官方校驗碼都比對不上這一層最精確的驗證,三者是一層疊一層往下查,而不是各自獨立的三個選項,遇到不同情境對照著挑,或是搭配著用都可以。

有一種情況三種方法搭配著用也還是查不到,就是前面提過的執行後自我清除型惡意程式。它做完事就把自己刪掉,不管是線上掃描、外掛掃描,還是手動比對雜湊值,事後都找不到痕跡,唯一能靠的是事發當下留下的異常紀錄,像是流量突然暴增、寄信量異常、資料庫裡多出陌生帳號,只能從這些線索往回推,而不是再重複掃一次。
掃描全部乾淨,網站行為仍然異常時的下一步
前面三種方法都做完,報告也都寫著乾淨,但網站還是持續跳轉、寄送垃圾郵件,或是主機商又來信警告,這是最讓人挫折的情況,而回到最前面那封警告信,遇到的很可能就是這種狀況。
第一個方向是升級到伺服器層級的掃描。前面三種方法查的都是應用層,也就是 WordPress 本身的檔案與資料庫,如果懷疑問題出在主機環境本身,比如同一台主機上的其他網站被感染,或是伺服器層級的排程任務被植入了什麼,這已經超出網站管理者自己能處理的範圍。Sucuri 也認為,最完整的做法是遠端掃描搭配伺服器端掃描一起做,而伺服器端掃描通常需要主機商或資安團隊協助才能執行。
第二個方向是交叉查詢黑名單狀態。用 VirusTotal 這類第三方工具,可以查到網站目前有沒有被 Google、防毒廠商這些主流廠商列入警示,這能反映外部看到的異狀,拿來跟自己掃描的結果互相印證。這裡有一件事得先分清楚,「網站被平台列入警示」跟「網站有沒有惡意軟體」是兩件不同的事,列入警示是外部的觀感問題,自己掃描顯示乾淨並不會自動解除警示,得另外送出審核申請才行,而且申請解除之前一定要先確認網站真的已經清乾淨,不然殘留的痕跡很容易讓審核被拒。
如果前面兩個方向都查過,還是沒有頭緒,異狀卻持續發生,很可能就是前面提到的執行後自我清除型惡意軟體在作怪。這種情況再重複掃一次也不會有新結果,能做的是即時監控流量、寄信量、登入紀錄這些數字,搭配時間點回推,抓出異常開始出現的那個時間點,再從那個時間點往回查是哪一支外掛、哪一次登入出了問題。
回到一開始那封主機商的警告信,真正該做的不是隨便裝一套掃描工具就假裝沒事,而是先想清楚自己的網站可能卡在哪一層,再挑對得上的方法去查。查不到答案時也別急著再裝下一套工具重複同樣的事,把力氣放在主機環境與即時監控上,通常才是真正能找到答案的地方。
