本文约 3,791 字,预计阅读 10 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:最怕的不是被黑,是慌乱中把证据删了
站点出事的那一刻,人的第一反应通常是”赶紧修好”。于是登录后台删掉可疑文件、回滚一下、重启服务器,站点恢复了,长舒一口气。两周后同样的事情再来一次,而且更严重——因为你从来没搞清楚攻击者是从哪个口子进来的,第一次的”修复”只是在打扫现场,门还开着。
更常见的问题是现场被破坏。有人发现被挂马后第一件事是删掉马文件,结果唯一能告诉你攻击入口的线索(文件创建时间、关联的上传记录、WebShell 的通信地址)全部消失。也有些人第一时间恢复备份,却没保留被污染的现场快照,事后无法复盘,也无法判断有没有数据泄露。应急处理的核心原则是:先止损、再取证、后恢复、最后复盘,顺序不能颠倒。本文按这个顺序讲清每一步做什么、跑什么命令。
原理分析:先分清是”故障”还是”入侵”
两类事件的处置路径完全不同,第一步必须先判断。故障的典型表现是:站点 502/504、数据库连不上、磁盘满、CPU 打满、插件更新后白屏。入侵的典型表现是:页面被跳转到陌生站点、前台出现博彩/药品内容、后台多了陌生管理员账号、搜索引擎提示”此网站可能已被黑客入侵”、服务器出现未知进程、邮件被大量外发。
判断的关键证据有几处:wp-content/uploads/ 下是否出现 .php 文件(正常上传目录不应有可执行脚本)、wp-admin 或 wp-includes 里是否有近期被修改的文件、数据库 wp_options 表里的 siteurl/home 是否被改、以及是否出现非官方的定时任务(WP-Cron 里的陌生 event)。
| 现象 | 更可能的原因 | 第一动作 |
|---|---|---|
| 502/504、打不开 | 资源耗尽、PHP-FPM 挂了、磁盘满 | 看错误日志,别急着重装 |
| 白屏(更新后) | 插件/主题冲突、PHP 版本不兼容 | 重命名插件目录 |
| 跳转到陌生站点 | 文件被篡改或数据库被注入 JS | 全站文件校验 |
| 前台多出垃圾内容 | 被写入大量垃圾文章 | 查后台账号与发布记录 |
| 后台多出管理员 | 账号泄露或注册功能被利用 | 立即停用并审计 |
| 服务器 CPU 长期 100% | 被植入挖矿脚本、被 CC | 查进程与外连 |
保姆级实操
- 第一步:止损,把损失范围固定住
这一步的目标是”不再继续恶化”,而不是修复。做法:把站点切到维护页(Nginx 返回 503 或直接放一个静态 maintenance.html);如果是数据库被篡改,先锁库只读;如果服务器被用作挖矿/发包,先断外网(保留内网以便取证)。同时把所有密码改一遍:服务器 SSH、数据库、WordPress 管理员、FTP、主机面板——改密码前先确认新密码不复用旧密码。
- 第二步:取证,先复制再动手
在改任何东西之前,把三样东西完整保存下来:整站文件(打包)、数据库导出、以及最近 7 天的 Web 访问日志与 PHP 错误日志。这一步的产出是后续所有分析的基础:
# 1) 打包整站文件(保留时间戳,便于按时间定位)
tar -czpf /root/backup-site-$(date +%F).tar.gz /var/www/html
# 2) 导出数据库(不要只导出单表)
mysqldump -u root -p --single-transaction --routines mimangfei > /root/db-$(date +%F).sql
# 3) 找出最近 7 天内被修改过的 php 文件(挂马最常伪装在这里)
find /var/www/html -type f -name "*.php" -mtime -7 -printf "%T@ %TY-%Tm-%Td %TH:%TM %p\n" | sort -rn
# 4) 上传目录里不该有 php,全部列出来
find /var/www/html/wp-content/uploads -type f -name "*.php" -ls
# 5) 用 WP-CLI 校验核心文件完整性(与官方 checksum 比对)
wp core verify-checksums
# 6) 访问日志里找可疑的 POST 请求(WebShell 通常会带特征参数)
awk '$6 ~ /POST/ {print $1, $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -40
# 7) 查按 IP 统计请求数,识别 CC 或扫描来源
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
- 第三步:定位入口
按优先级排查四个入口:一是过期的插件/主题(最常见),看有没有超过一年未更新的商业插件;二是弱口令(admin/123456 之类),查日志里 wp-login.php 的失败记录;三是服务器上的其他站点(同服务器旁的站点被攻破后跨目录);四是备份文件泄露(/backup.sql、www.zip 放在 web 根目录下可被直接下载)。用 wp core verify-checksums 确认核心干净,再用上面第 3、4 条命令定位具体文件。
- 第四步:干净恢复
不要”修修补补”,直接干净恢复:用被入侵前的一个干净备份还原(先确认那个备份本身是干净的——用同样的校验命令扫一遍)。如果备份不可用,则从官方重新下载 WordPress 核心包覆盖 wp-admin 与 wp-includes,重新安装所有插件与主题(从官方源下载,不要用”备份里的老版本”),数据库则逐表检查后导入。恢复后立刻:重置所有用户密码、重新生成 wp-config.php 里的 AUTH_KEY 等盐值(这会让所有已登录会话失效)、删除并重建 salt 后通知用户重新登录。
- 第五步:加固与复盘
加固动作清单:目录权限设为文件 644、目录 755,wp-config.php 设为 600 或 440;在 Web 服务器层禁止 uploads 目录执行 PHP;后台加双因素认证;限制 wp-login.php 的访问频率;关闭文件编辑(DISALLOW_FILE_EDIT);安装安全插件(Wordfence、Sucuri 或 Defiant 系)做持续扫描。
<?php
// wp-config.php 中建议常开的两项
define( 'DISALLOW_FILE_EDIT', true ); // 禁止后台编辑主题/插件文件
define( 'WP_DEBUG', false ); // 生产环境关闭调试输出,避免泄露路径
define( 'FORCE_SSL_ADMIN', true ); // 后台强制 HTTPS
# Nginx:禁止上传目录执行 PHP(加在 server 块内)
location ~* ^/wp-content/uploads/.*\.php$ {
deny all;
access_log off;
log_not_found off;
}
# 隐藏备份与敏感文件
location ~* \.(sql|bak|old|zip|tar|gz|log)$ {
deny all;
}
- 写一份事故报告
内容是:什么时候发现的、现象是什么、影响范围(有无数据泄露、停机时长)、根因是什么、做了什么、还剩什么没做。这份报告决定了后续会不会再犯。
常见踩坑
- 发现被黑后立刻删除马文件:现场证据消失,永远不知道入口在哪,必然二次被黑。
- 案例:某站点前台被跳转到博彩站,运维在后台把主题文件”恢复默认设置”后跳转消失,两天后再次被挂马,如此反复四次。后来用
wp core verify-checksums加文件时间排查,发现是一个已停用但未删除的两年前的旧插件里的upload-handler.php被反复写入——停用不删除的插件同样是活的后门。 - 恢复到”最近一次备份”而不验证备份是否干净:备份里同样有马,恢复完立刻复发。
- 只改 WordPress 密码,没改服务器和数据库密码:攻击者从 SSH 进来,改 WP 密码毫无意义。
wp-config.php权限 777:同服务器的其他站点可直接读取数据库账号。- 生产环境开着
WP_DEBUG:报错信息把绝对路径、数据库名直接展示给访客。 - 恢复后没重新生成 AUTH_KEY:已窃取的登录 cookie 依然有效。
总结
应急的判断标准是”能不能说清根因”。说不出来的处置都只是打扫现场。正确的顺序永远是:止损 → 取证 → 定位入口 → 干净恢复 → 加固复盘,其中取证一步最容易被跳过,也最关键。日常要做的就是两件低成本的事:每周一次异地备份并定期验证可恢复、给后台加双因素认证。把这两项做好,再配合 网站维护年费与续费管理清单 里的监控告警,大部分事故能在造成损失前被发现。
