網站頻繁被植入 WebShell 是典型的「修補漏洞不徹底、權限管控不到位、入侵痕跡未清乾淨」綜合症。本文提供從掃描、溯源、清理到系統加固的標準化 SOP,適用於 Discuz!、WordPress、Laravel 等 PHP 架構站點。
# 常見單行木馬特徵
find /www -type f -name "*.php" | xargs grep -l "eval(" > /tmp/scan_eval.txt
find /www -type f -name "*.php" | xargs grep -l "assert(" > /tmp/scan_assert.txt
find /www -type f -name "*.php" | xargs grep -l "preg_replace.*\/e" > /tmp/scan_preg.txt
# 合併去重
cat /tmp/scan_*.txt | sort -u > /tmp/webshell_suspect.txt
# 進階:多特徵一次掃描
grep -r -E "eval\(|assert\(|passthru\(|system\(|shell_exec\(|base64_decode\(|gzuncompress\(" /www --include="*.php" > /tmp/scan_full.txt
# 列出 7 天內被修改的 PHP 檔
find /www -type f -name "*.php" -mtime -7 -printf "%TY-%Tm-%Td %TH:%TM %p\n" | sort > /tmp/recent_modified.txt
# 列出 7 天內被建立的檔
find /www -type f -name "*.php" -ctime -7 -printf "%TY-%Tm-%Td %TH:%TM %p\n" | sort > /tmp/recent_created.txt
部署 scanner.php 至網站根目錄(僅內網/IP 白名單可存取),功能:
data/、images/ 等靜態目錄。eval, assert, exec, system, passthru, shell_exec, proc_open, popen, curl_exec, base64_decode, gzuncompress, str_rot13。完整腳本較長,建議參考開源專案:php-malware-scanner 或 NAXSI WAF 規則集。
grep -i "POST.*\.php" /var/log/nginx/access.log | grep -v "200 0" | head -50plugin.php 注入、WordPress File Manager 外掛 RCE)。tar -czf /backup/hacked_$(date +%F).tar.gz /www(保留證據)。diff -ru /www /path/to/clean_source 找出被植入/竄改的核心檔。crontab -l、ls /etc/cron.*/、systemctl list-timers,清除惡意定時任務。cat ~/.ssh/authorized_keys、/root/.ssh/authorized_keys,移除不明公鑰。; php.ini
disable_functions = exec,passthru,shell_exec,system,proc_open,proc_close,proc_get_status,proc_nice,proc_terminate,popen,pclose,pcntl_exec,pcntl_fork,pcntl_signal,pcntl_alarm,pcntl_wait,pcntl_waitpid,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_signal_dispatch,dl,syslog,openlog,closelog,apache_child_terminate,apache_get_modules,apache_get_version,apache_getenv,apache_lookup_uri,apache_note,apache_request_headers,apache_reset_timeout,apache_response_headers,apache_setenv,getallheaders,virtual,mail,mb_send_mail,ini_set,ini_alter,ini_restore,set_time_limit,set_magic_quotes_runtime,magic_quotes_runtime,stream_socket_server,stream_socket_client,fsockopen,pfsockopen,curl_exec,curl_multi_exec,parse_ini_file,show_source,highlight_file,escapeshellarg,escapeshellcmd,readlink,symlink,link,popepassthru
視業務需求酌情保留(如需發信保留 mail、需爬蟲保留 curl_exec 但需程式碼層面嚴格驗證 URL)。
; php.ini 或 .user.ini (每站點獨立)
open_basedir = /www:/tmp:/var/lib/php/session
display_errors = Off
log_errors = On
error_log = /var/log/php/php_error.log
error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT
session.cookie_httponly = 1
session.cookie_secure = 1 ; 僅 HTTPS
session.use_strict_mode = 1
session.cookie_samesite = "Strict"
session.gc_maxlifetime = 1440
session.save_path = "/var/lib/php/session"
# nginx.conf - 放在 location ~ \.php$ 之前!
location ~ ^/(data|images|static|uploads|attachment|avatar)/.*\.php$ {
deny all;
return 403;
}
# 正常 PHP 處理
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
server_tokens off;
header X-Powered-By "";
client_max_body_size 10m;
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req zone=api burst=20 nodelay;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.bootcss.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https:; connect-src 'self'; frame-ancestors 'self';";
| 項目 | 建議配置 |
|---|---|
| SSH | 禁用密碼登入 PasswordAuthentication no、禁用 Root 登入 PermitRootLogin no、更改預設埠 22、啟用 Fail2Ban |
| 防火牆 | 僅開放 80/443/SSH 埠、資料庫埠僅允許本機/內網存取 |
| MySQL | 建立唯讀/讀寫分離帳號、禁用 LOAD DATA LOCAL INFILE、啟用審計日誌、定時備份驗證 |
| 檔案系統 | 網站目錄擁有者 www:www、權限 755/644、上傳目錄 755 但掛載 noexec,nosuid,nodev |
| 監控告警 | 部署 Prometheus + Node Exporter + Grafana、日誌集中化 (ELK/Loki)、檔案完整性監控 (AIDE/Tripwire) |
發現異常
│
▼
隔離流量 (503/WAF封禁)
│
▼
離線完整備份 (證據保全)
│
▼
掃描定位 (特徵碼 + 時間線 + MD5比對)
│
▼
溯源入口 (存取日誌 + 應用日誌 + 弱點掃描)
│
▼
清理木馬 + 修復漏洞 + 變更所有密鑰
│
▼
多層加固 (PHP + Nginx + 系統 + 資料庫)
│
▼
驗證上線 + 持續監控 + 事後複盤報告
「防禦深度」勝過「單點防護」。木馬能寫入,必有「寫入入口(上傳/注入/弱密碼)」與「執行環境(PHP 解析權限)」兩個條件同時滿足。切斷任一條件即可阻斷攻擊鏈:
1. 修補應用漏洞 → 封堵寫入入口
2. 目錄禁用 PHP 執行 → 切斷執行環境
3. 最小權限原則 → 限制損害範圍
4. 完整日誌與監控 → 縮短發現時間