发布时间:2026-09-25 12:31 更新时间:2026-09-25 12:31 阅读量:0
某天你打开网站目录,发现根目录里多了几个从没见过的 PHP 文件,名字可能是一串随机字母,也可能是伪装成 index2.php、upload.php 这种看着眼熟的名字。先别急着直接删——删掉只能解决这一次,上传入口还在,过几天还会回来。正确的顺序是先固定现场,再用文件时间线、文件属主属组、Web 访问日志三条线索交叉比对,找出文件是怎么进来的,最后配合上传目录禁止执行 PHP 做加固。下面按这个思路一步步说。
发现异常文件后,第一件事不是删,而是把它的元信息记录下来。用 ls -l --time-style=full-iso 看修改时间、属主属组、权限位,再用 stat 看三个时间戳:mtime(内容最后修改)、ctime(元数据变更)、atime(最后访问)。被写入的文件 mtime 通常就是上传发生的时刻,这个时间点非常关键。
# 查看可疑文件的时间与属主
ls -l --time-style=full-iso /www/wwwroot/example.com/
stat /www/wwwroot/example.com/suspicious.php
列出最近 3 天内被修改过的 php 文件(按时间倒序)
find /www/wwwroot/example.com/ -type f -name "*.php" -mtime -3 -printf '%TY-%Tm-%Td %TH:%TM %u:%g %p\n' | sort -r
列出最近 3 天内新增的目录(上传往往先建目录)
find /www/wwwroot/example.com/ -type d -mtime -3 -printf '%TY-%Tm-%Td %TH:%TM %p\n' | sort -r
把时间线拉出来之后,你会看到一批同一分钟内被创建或修改的文件。它们大多集中在某个目录下,那个目录就是可疑的上传落地点。注意:如果服务器做过 rsync、宝塔备份还原之类的操作,mtime 可能被保留成旧时间,这时要结合 ctime 一起看,具体以实际环境为准。
接着看属主。宝塔面板下 Web 进程一般以 www 用户运行(Nginx 与 PHP-FPM 的属主可能不同,视配置而定)。如果可疑文件属主是 www,说明它是通过 Web 请求写入的,方向就往上传接口、表单、插件漏洞上找;如果属主是 root 或某个登录用户,则要考虑是不是 FTP、SSH 账号被用了,或者面板被登录过。
# 查看指定目录下所有文件的属主分布,快速定位异常归属
find /www/wwwroot/example.com/uploads/ -type f -printf '%u:%g\n' | sort | uniq -c | sort -rn
找出权限位异常的文件(比如 777 或带 s 位)
find /www/wwwroot/example.com/ -type f \( -perm -o+w -o -perm -4000 \) -ls
检查 PHP 进程运行用户,确认 Web 写入的身份
ps -eo user,cmd | grep -E 'php-fpm|nginx' | grep -v grep
同时留意目录权限。上传目录如果被设成 777,任何进程都能写;正常做法是目录 755、文件 644,属主为 Web 运行用户。权限过宽本身不直接等于被入侵,但会放大风险,也让你事后分不清是谁写的。
有了时间点,就去 Nginx 访问日志里找这个时刻附近的请求。宝塔的站点日志一般在 /www/wwwlogs/ 下,按域名命名。重点看 POST 请求、返回 200 的上传类路径,以及带可疑参数的 GET。可以用 awk 按时间范围过滤,再按状态码和请求方法排序。
# 在站点日志中筛出指定时间段的 POST 请求(示例:9月10日 14:00-14:10)
awk '$4 ~ /\[10\/Sep\/2026:14:0[0-9]/ && $6 ~ /POST/ {print $1, $4, $6, $7, $9}' \
/www/wwwlogs/example.com.log | sort
统计返回 200 且路径含 upload 的请求,看谁在上传
awk '$6 ~ /POST/ && $7 ~ /upload/ && $9 == 200 {print $1, $4, $7}' \
/www/wwwlogs/example.com.log | sort | uniq -c | sort -rn | head -20
找出访问量异常的单个 IP(正常站点一般不会短时间内大量 POST)
awk '$6 ~ /POST/ {print $1}' /www/wwwlogs/example.com.log | sort | uniq -c | sort -rn | head
把时间线和日志对齐后,通常能定位到某个具体的上传接口或某个伪装成图片上传的请求。如果日志里该时段全是 CDN 节点 IP,需要在 Nginx 里配好 real_ip 还原真实访客 IP,否则日志会失去参考价值。如果站点开了 CDN 又没做还原,建议先补上再看日志。
还有几个容易忽略的入口要一并检查:宝塔面板自身的登录日志、FTP 的登录记录、以及 WordPress 后台的插件与主题版本。很多异常 PHP 文件是通过过期插件或主题的上传功能落地的,升级到官方最新版本是必要动作,具体版本号以官方发布为准。
查清入口后,除了删掉异常文件、改掉相关账号口令,更要做的是让上传目录即使被写入文件也无法执行。Nginx 中可以通过 location 匹配上传目录,把 PHP 请求直接拒绝。注意 location 的匹配优先级:~ 是区分大小写的正则,放在 server 块里的位置会影响是否被前面的规则截走,建议实测确认。
# 在站点 server 块中加入,禁止 uploads 目录执行 PHP
location ~* ^/uploads/.*\.(php|php5|phtml)$ {
return 403;
}
另一种写法:把上传目录下的 php 交给静态处理,直接返回 404
location ~* ^/(uploads|images|attachments)/ {
location ~ \.php$ {
return 404;
}
}
改完配置记得用 nginx -t 校验语法,再 nginx -s reload 或通过宝塔面板重载。验证时不要只看配置有没有报错,要真的去请求一个上传目录下的测试 PHP 文件,确认返回 403 或 404 而不是被解析执行。另外,WordPress 的 wp-content/uploads 目录本身不需要执行 PHP,禁执行一般不会影响正常功能;但某些插件依赖上传目录做中转,改动前先在测试环境确认。
整套流程可以概括成一句话:先留证据(时间线、属主、日志),再断入口(升级组件、收紧权限、上传目录禁执行),最后做验证。排查完别急着收工,建议把这次的时间点、IP、路径记录下来,过一周再跑一次 find 时间线命令复查,确认没有新的异常文件出现。如果反复出现同类文件,说明入口没堵干净,需要回到日志环节重新比对,而不是反复删除文件。
| 📑 | 📅 |
|---|---|
| WordPress定时任务被wp-cron拖慢:改用系统crontab配置与验证 | 2026-09-25 |
| 宝塔面板网站备份还原到另一台服务器:跨机迁移的完整流程 | 2026-09-25 |
| WordPress文章ID与固定链接优化:伪静态改动后旧链接兼容 | 2026-09-25 |
| MySQL只允许本机连接后网站报错:bind-address与用户host授权取舍 | 2026-09-25 |
| PHP-FPM 进程数怎么调:pm.max_children 与内存换算、502 反复排查 | 2026-09-25 |
| Nginx map 指令实战:按 UA、Referer 与域名做条件分流 | 2026-09-25 |
| CDN与源站双重缓存导致改版不生效:三层缓存刷新顺序 | 2026-09-26 |
| WordPress中文名附件变乱码或404:编码链路排查 | 2026-09-26 |
| 宝塔面板升级后网站打不开:PHP扩展、Nginx配置与面板服务逐项回滚排查 | 2026-09-26 |
| 域名下PC站与m站SEO冲突:canonical、自适应与UA跳转取舍 | 2026-09-26 |