WordPress 安全加固实操(2026):关掉 XML-RPC、限住登录爆破,顺手查一遍后门

小站平时没什么人惦记,一被惦记就是两种剧本:xmlrpc.php 被拿来撞账号,或者后台登录页被脚本按着跑字典。这些流量不经过网页界面,你在后台看访问统计基本看不出来,等发现时通常已经是数据库里多出个管理员,或者上传目录里躺了一个陌生的 php 文件。

我后来把能做的事都挪到了服务器层,不用安全插件。理由很实际:加固写在 Nginx 和 wp-config.php 里,不占内存也不参与 WordPress 的加载流程,插件本身出漏洞或者互相打架的概率直接归零。

下面这套我在自己的站上跑通了,一共六步,每步都带上验证命令。

动手前:先留回滚的路

改配置之前把两样东西备份出来,数据库和 Nginx 配置,缺一个后面都可能尴尬。

cd /www/wwwroot/www.yoursite.com
wp db export ~/backup-site-$(date +%F).sql
cp /www/server/panel/vhost/nginx/www.yoursite.com.conf ~/nginx-conf-$(date +%F).bak

站点配置文件的具体路径以你的面板为准,宝塔一般就在 /www/server/panel/vhost/nginx/ 下面跟域名同名,面板「网站 → 设置 → 配置文件」打开的是同一份。改完一定要先测再重载,nginx -t 报错就说明语法有问题,这时候别 reload,改回去再说:

nginx -t && nginx -s reload

顺带一句,SSH 别关,加固过程中最容易把自己锁在门外的就是「改了访问控制忘了留通道」。

第一步:关掉 XML-RPC

这个接口老到可以追溯到手机端发文,现在的站点基本用不上,但它支持一次请求里批量试几百个账号密码,被扫的时候很省对方的事。Nginx 里加一行精确匹配就够了,精确匹配的优先级高于正则,所以不挑位置:

location = /xmlrpc.php { deny all; access_log off; }

验证:

curl -s -o /dev/null -w "%{http_code}\n" https://www.yoursite.com/xmlrpc.php

返回 403 就成了。注意验证要在源站上打,站点前面挂了 CDN 或者 阿里 ESA 这类边缘加速的话,你在浏览器里拿到的那份可能不是当前配置的响应,用这条从服务器本机打回源站更准:

curl -s -o /dev/null -w "%{http_code}\n" -H "Host: www.yoursite.com" http://127.0.0.1/xmlrpc.php

第二步:给登录页限流

限流要写两处,limit_req_zone 定义在 http 块里,limit_req 施加在具体 location 上。http 块的位置找不到就用 nginx -V--conf-path 指哪儿了。

# http 块内,宝塔是 /www/server/nginx/conf/nginx.conf
limit_req_zone $binary_remote_addr zone=wplogin:10m rate=5r/m;

关键是 rate 的单位:r/s 是每秒,低于每秒的才写 r/mrate=5r/m 就是平均每分钟 5 次请求。

接着动站点配置。宝塔的站点配置里本来就有一段 location ~ \.php$ { ... fastcgi ... },把整段复制一份,只把 location 那一行换成精确匹配,再加两行限流指令,fastcgi 部分保持原样。

location = /wp-login.php {
    limit_req zone=wplogin burst=5 nodelay;
    limit_req_status 429;
    # 下面照抄原配置里 location ~ \.php$ 那段的内容
}

burst=5 是允许的突发量,nodelay 让突发请求直接处理而不是排队慢慢放,limit_req_status 默认是 503,改成 429 语义更明确。验证方法是连打十几次登录请求,看返回码有没有从 200 变成 429:

for i in $(seq 1 12); do
  curl -s -o /dev/null -w "%{http_code} " -X POST \
    -d "log=admin&pwd=test" https://www.yoursite.com/wp-login.php
done; echo

这里有个真实的坑:限流按 IP 算,公司、学校、家里的宽带出口往往是共用 IP,rate 调太紧会把正常读者也一起挡掉。5r/m 加 burst 5 这个量级够用,别一上来就写 1r/m。

第三步:锁掉后台的代码编辑器

后台「外观 → 主题文件编辑器」能直接改 PHP 文件,这个功能一旦落到别人手里,等于白送一个 webshell。用 WP-CLI 写常量比手改文件稳,--raw 表示按字面量写进去,不要加引号:

wp config set DISALLOW_FILE_EDIT true --raw

想连「后台装插件改主题」一起收掉的,再加一条:

wp config set DISALLOW_FILE_MODS true --raw

第二条要权衡,它同时会切断后台的一键更新,之后升级只能走命令行(wp core update / wp plugin update --all)。我现在是开着它,反正更新本来就习惯用命令行。

顺手把 wp-config.php 的权限收紧,这个文件里有数据库密码和各种盐值:

chmod 600 wp-config.php
chown www:www wp-config.php

验证最简单的方式是回后台刷新一下「外观」菜单,主题文件编辑器那项应该消失了。

第四步:禁止 uploads 目录执行 PHP

上传目录是用来放图片的,任何在这里执行的 PHP 都值得怀疑。加一条正则拒绝:

location ~* ^/wp-content/uploads/.*\.php$ { deny all; }

这段必须写在 location ~ \.php$ 前面。Nginx 的正则 location 按在配置文件里出现的先后顺序匹配,先命中的生效,写在后面的话,请求会被上面那条 PHP 规则先接走,照样执行。

验证方式是自己造一个文件试:

echo '<?php echo "should-not-run";' > /www/wwwroot/www.yoursite.com/wp-content/uploads/t.php
curl -s -o /dev/null -w "%{http_code}\n" https://www.yoursite.com/wp-content/uploads/t.php
rm /www/wwwroot/www.yoursite.com/wp-content/uploads/t.php

403 就对了,最后一行别忘,测试文件要删掉。

第五步:查一遍站里有没有后门

前面几步是堵门,这一步是查有没有人已经进来过。WP-CLI 自带文件完整性校验,拿官方仓库的哈希值比对本地文件:

wp core verify-checksums
wp plugin verify-checksums --all

core 那条会列出被改动或多余的文件,plugin --all 只能校验 WordPress 官方仓库里的插件,第三方来源或者改过代码的插件会被标出来,标出来不等于有后门,但值得逐个看。再补两条人工排查,看最近七天被动过的 PHP 文件,以及后台账号和计划任务:

find /www/wwwroot/www.yoursite.com -name "*.php" -mtime -7 -ls
wp user list --role=administrator
wp cron event list

真正需要紧张的特征有几个:uploads 或 upgrade 目录下冒出 PHP 文件、名字像 index1.phpwp-cache-xxx.php、管理员列表里多出一个看着像临时账号的用户、计划任务里挂着不认识的回调。清之前把可疑文件下载一份留档,别直接删。

回滚怎么写回去

Nginx 那边,语法写错 nginx -t 就会拦住你,真重载挂了就用备份覆盖回去,再 nginx -s reload

cp ~/nginx-conf-2026-09-21.bak /www/server/panel/vhost/nginx/www.yoursite.com.conf
nginx -t && nginx -s reload

wp-config 的常量想撤销,用 delete 比手改安全:

wp config delete DISALLOW_FILE_EDIT

还有一件事,配置生效只代表源站生效,站点前面有边缘缓存的话去刷一下,否则访客拿到的还是旧的那份。这套加固做完,服务器层基本没什么好再抠的了,剩下更值得花时间的是备份的可靠性。我上次把整个站搬了一次家,那份流程里写清了备份和回滚该怎么做。真出事的时候,能不能恢复比重不重要得多。

© 版权声明
THE END
喜欢就支持一下吧
点赞12 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容