用 nginx 在源站封掉 WordPress 的高危入口(含 XML-RPC)-第三维度MokIII

默认装的 WordPress,开着一堆没人用的入口

刚装好的 WordPress,公网上能直接访问到这些东西:

  • /readme.html —— 带版本信息的欢迎页
  • /license.txt —— 许可证原文
  • /wp-admin/install.php —— 安装向导
  • /wp-includes/ 下的 PHP 文件
  • /xmlrpc.php —— 老式接口,暴力破解的最爱

它们对正常访客没有任何用,但会告诉扫描器"这里是 WordPress,版本是 X"。下面是我在源站用 nginx 把这一批封掉的写法。

一、先量一下封之前暴露了多少

for u in /readme.html /license.txt /wp-admin/install.php /xmlrpc.php; do
  printf '%-28s %s\n' "$u" "$(curl -s -o /dev/null -w '%{http_code} %{size_download}B' https://www.example.com$u)"
done

我这边实测:readme.html 7407 B、license.txt 19903 B、xmlrpc.php 对 POST 返回 200。全部公网可达。

二、规则

放在站点配置的独立文件里(用 00- 前缀保证排在最前):

# 1) 版本信息与许可证
location = /readme.html { return 403; }
location = /license.txt { return 403; }

# 2) 安装向导
location ~ ^/wp-admin/install.php(/|$) { return 403; }

# 3) wp-includes 下的 PHP —— 但必须放行 /wp-includes/js/
location ~* ^/wp-includes/(?!js/).*\.php(/|$) { return 403; }

# 4) 主题目录里的 PHP 不该被直接执行
location ~* ^/wp-content/themes/.*\.php(/|$) { return 403; }

第 3 条的负向断言不能省

(?!js/) 是必须的:/wp-includes/js/ 下面有通过 PHP 输出的 JS(老编辑器的 wp-tinymce.php 就是典型)。如果你图省事一刀切,编辑器会坏,而且报错信息很不直观。

千万别顺手封 /wp-content/plugins/

很多人抄规则时会把插件目录也封掉,这是要出事的——不少插件有合法的 HTTP 端点(回调、通知、接口),一封就是莫名其妙的功能失效。

三、顺手把 XML-RPC 也拦掉

XML-RPC 的问题不是漏洞,而是它允许一次请求尝试大量账号密码,是暴力破解的加速器。如果站点不用 Jetpack、不用手机客户端发文章、没开 pingback,可以直接在源站拒绝:

location ~* ^/xmlrpc.php(/|$) { deny all; return 403; }

为什么要在 nginx 拦、而不是靠 WAF 规则? 因为 WAF 规则依赖正则匹配请求,难免有绕过写法(大小写、双斜杠、加 query)。nginx 的 location 匹配发生在请求进 PHP 之前,写对了就是确定性的。

判断能不能拦,先确认依赖:

# 站点有没有用到 XML-RPC 的功能(Jetpack / pingback / 客户端)
grep -ri "xmlrpc" /www/wwwroot/example.com/wp-content/plugins/ --include=*.php -l | head

四、把缓存目录设成 internal

如果站点用了 WP Super Cache 之类的静态直出,缓存目录应该只允许内部跳转访问,不允许外部直接请求:

location ~* /wp-content/cache/ { internal; }

internal 的含义是:只允许 nginx 内部 rewrite / X-Accel-Redirect 转到它,外部直接访问会得到 404。这样缓存文件不会成为"可以绕过去直连的一份内容"。

五、验证

# 全部应为 403
for u in /readme.html /license.txt /wp-admin/install.php /xmlrpc.php; do
  printf '%-28s %s\n' "$u" "$(curl -s -o /dev/null -w '%{http_code}' https://www.example.com$u)"
done

# 编辑器仍要能工作(这条不能被误伤)
curl -s -o /dev/null -w '%{http_code}\n' https://www.example.com/wp-includes/js/tinymce/wp-tinymce.php

# 缓存目录外部直连应 404
curl -s -o /dev/null -w '%{http_code}\n' https://www.example.com/wp-content/cache/supercache/www.example.com/index-https.html

回归自检一定要做:这类规则最容易"封对了攻击面,也封坏了功能",尤其是第 3、4 条。

六、回滚

删掉独立配置文件 → nginx -t && nginx -s reload。不要直接改 vhost 主体——面板重新生成站点配置时会把你的改动抹掉。


一句话总结:WordPress 默认暴露的一堆入口对访客没用、对扫描器很有用。用 nginx 在源站封掉最省事、最确定,但三条边线要守住:放行 /wp-includes/js/、别封插件目录、封完必须回归自测。