Cnoos

记录一些学习、工作、生活的内容或想法

ProtectRequest:为 WordPress 私人站点设计一套轻量请求防护体系

项目 约 11 分钟 阅读 15

本篇目录
  1. 一、为什么没有直接依赖现成安全插件
  2. 二、当前防护体系的分层
  3. 三、从日志中看到的真实攻击类型
  4. 四、ProtectRequest 当前承担的职责
  5. 五、为什么不把所有安全规则都塞进去
  6. 六、阶段性结论与后续方向

在维护 WordPress 站点的过程中,我逐渐意识到一个问题:很多攻击并不是复杂的入侵,而是大量自动化扫描。

它们会不断尝试访问各种文件或路径,例如:

/xmlrpc.php
/.env
/wp-config.php
/.git/
随机 php 文件
/wp-content/plugins/xxx/shell.php
/wp-json/wp/v2/users

这些请求大多数不会真正成功,但它们会带来三个问题:

第一,制造大量无意义访问日志。
第二,让 PHP 和 WordPress 处理本不该处理的请求。
第三,在某些配置不严谨的情况下,可能暴露站点结构或形成真实风险。

ProtectRequest 就是在这样的背景下逐步形成的。

它最初并不是为了替代 Wordfence、NinjaFirewall 这类成熟安全插件,也不是为了做一个通用开源项目,而是为了给我自己的 WordPress 站点增加一层可理解、可控制、可复盘的请求防护能力。


一、为什么没有直接依赖现成安全插件

WordPress 生态中已经有很多成熟安全插件,例如 Wordfence、AIOS、NinjaFirewall、BBQ Firewall 等。它们的功能更完整,规则库也更成熟。

但我的需求并不是安装一个“大而全”的安全套件,而是希望在自己的站点里实现一套更轻量、更明确的防护逻辑:

哪些请求应该在 Nginx 层直接拒绝?
哪些请求应该由 PHP 插件动态判断?
哪些只是 404 噪音?
哪些需要记录但不必立刻封禁?
哪些 IP 应该临时封禁?
哪些行为应该永久封禁?

对我来说,ProtectRequest 的价值不只是“拦截攻击”,更重要的是帮助我理解站点正在面对什么样的访问行为,并把这些行为转化为可管理的安全规则。


二、当前防护体系的分层

目前站点防护大致分为三层:

请求
 ↓
Nginx
 ↓
PHP ProtectRequest
 ↓
WordPress

其中 Nginx 负责更底层、更静态的拦截,例如:

敏感文件访问
协议异常
目录访问
PHP 执行入口限制
xmlrpc.php 前置拦截

ProtectRequest 则负责更贴近 WordPress 的动态判断,例如:

可疑 User-Agent
敏感路径探测
随机 PHP 文件扫描
REST API 用户枚举
作者枚举
IP 封禁
Redis 状态管理
后台封禁列表

WordPress 本身只应该处理真正正常的业务请求,而不是被迫处理大量扫描器请求。

这个分层思路非常重要。

如果所有规则都放到 PHP 层,会导致无效请求仍然进入 WordPress 环境,浪费资源。
如果所有规则都放到 Nginx 层,又会缺少动态判断、后台管理和可视化分析。

所以更合理的方式是:

Nginx:拦截明显非法请求
ProtectRequest:处理动态识别和封禁管理
WordPress:只处理正常业务

三、从日志中看到的真实攻击类型

在日志分析中,我看到的请求大致可以分为几类。

1. 敏感文件探测

这类请求通常访问:

/.env
/wp-config.php
/wp-config.php.bak
/.git/config
/.ssh/
composer.lock
settings.py

它们的目的很明确:寻找配置文件、数据库密码、部署信息或源码泄露。

这类请求不应该进入 WordPress,也不应该被视为普通 404。
对于 .env、wp-config、.git 这类路径,我倾向于直接视为高风险行为,触发永久封禁。


2. XML-RPC 滥用

WordPress 的 /xmlrpc.php 是常见攻击入口,经常被用于暴力破解、批量请求或探测。

在当前站点中,XML-RPC 没有实际业务需求,因此 ProtectRequest 已经对它进行拦截。后续更适合将这类规则前移到 Nginx 层:

location = /xmlrpc.php {
    return 403;
}

这样可以避免大量 XML-RPC 请求进入 PHP。


3. 随机 PHP / Webshell 文件名扫描

当我分析日志时也能看到大量类似这样的请求:

/xa.php
/dr.php
/yj09.php
/xstelth.php
/ups.php
/r00t.php
/saim11.php
/wp-includes/css/index.php
/wp-admin/css/index.php
/wp-content/plugins/hellopress/wp_filemanager.php

这些请求大多数返回 404,因为文件本身不存在。
但它们的意图并不是访问正常页面,而是在寻找已经被上传或遗留的 Webshell。

这类请求短期看风险不高,但它暴露出一个重要问题:

如果未来某个上传漏洞、插件漏洞或误操作导致恶意 PHP 文件落入 wp-content 目录,
攻击者就可能通过这些路径直接执行它。

因此,真正可靠的处理方式不是只在 ProtectRequest 里匹配文件名,而是在 Nginx 层限制 PHP 执行入口,例如:

location ~* ^/(wp-content|wp-includes)/.*.php$ {
    return 403;
}

这样即使未来某个目录下出现 PHP 文件,也不会被外部直接访问。


4. 499 客户端断开请求

部分扫描器会快速发送请求后立即断开连接,在 Nginx 日志中表现为 499。

这类行为会影响 PHP 层封禁的及时性。因为如果客户端过早断开,请求可能没有完整进入后续处理流程,ProtectRequest 的封禁逻辑不一定每次都来得及执行。

但从实际结果看,只要该 IP 后续命中敏感路径规则,例如 config,ProtectRequest 仍然能够识别并永久封禁。

这说明 499 并不是完全绕过防护,而是提醒我:对于高频扫描和明显恶意 IP,最好尽量在 Nginx 层提前处理。


5. 异常 200 响应

日志中还有一类更值得注意的请求:本不该有意义,却返回了 200。

例如:

/wp-content/index.php
/wp-content/plugins/index.php
/wp-links-opml.php
POST /

这些不一定是漏洞,但它们会给扫描器反馈:

站点是 WordPress
目录结构存在
某些默认文件可访问
首页可以接受脚本 POST

对于这类请求,我更倾向于减少信息反馈。例如将部分 WordPress 占位文件直接返回 403:

location = /wp-content/index.php {
    return 403;
}

location = /wp-content/plugins/index.php {
    return 403;
}

location = /wp-content/themes/index.php {
    return 403;
}

location = /wp-includes/index.php {
    return 403;
}

四、ProtectRequest 当前承担的职责

ProtectRequest 当前更像是一个轻量请求防护模块,而不是完整安全插件。

它主要承担以下职责:

1. 请求路径检测
2. 可疑 User-Agent 检测
3. 敏感文件探测识别
4. XML-RPC 拦截
5. REST API 用户枚举防护
6. IP 临时封禁 / 永久封禁
7. Redis 封禁状态存储
8. 后台封禁列表展示
9. 手动封禁和解封
10. 攻击原因记录

它的价值在于形成了一个闭环:

发现恶意请求
 ↓
记录原因
 ↓
写入 Redis
 ↓
后续请求自动拦截
 ↓
后台可查看和解封

这比单纯在 functions.php 中写几条拦截规则更有管理价值。


五、为什么不把所有安全规则都塞进去

在设计过程中,我也考虑过是否应该把其他安全能力全部集中到 ProtectRequest。

例如:

登录失败限速
邮箱验证
设备信任
评论审核
REST 权限控制
安全响应头
上传目录扫描
Nginx 规则管理

但最后我认为,ProtectRequest 不应该变成一个“大而全”的安全插件。

原因是不同安全能力所属的层级不同:

登录失败限速:属于账户系统
邮箱验证:属于身份验证系统
设备信任:属于登录风控
Nginx 规则:属于 Web Server 层
请求识别和封禁:属于 ProtectRequest

如果所有逻辑都塞进一个插件,短期看集中,长期看会变得混乱:

职责变重
维护困难
误封风险增加
性能开销变大
开源价值降低
一处出错影响全站

所以当前更合理的定位是:

ProtectRequest 可以成为安全状态的管理中心,
但不应该成为所有安全逻辑的唯一执行者。

六、阶段性结论与后续方向

目前 ProtectRequest 已经基本成型并上线,它的很多数据来自真实站点环境,包括自己的访问日志、攻击样本、Redis 封禁设计、Nginx 配置,以及 WordPress 主题和账户系统。

但它暂时并不适合作为通用插件开源。如果直接面向更多站点,就需要处理不同主机环境、Redis 降级、Apache / Nginx 差异、WooCommerce / REST API 误封、公开注册站点适配等问题。同时,安全规则完全公开后,也可能暴露自己的防护边界。

所以我暂时不会把它包装成一个“大而全”的安全插件,也没有必要让它和 Wordfence、NinjaFirewall 这类成熟插件正面对比。对我来说,ProtectRequest 更适合被定位为:一个面向私人 WordPress 站点的轻量请求防护与 IP 封禁管理模块。

后续如果继续增强,我可能会加入以下内容:

风险评分机制
可疑请求观察日志
随机 PHP 探测识别
敏感文件规则库增强
Nginx 建议规则生成
uploads 目录危险 PHP 文件巡检
封禁等级升级机制
攻击分类统计

ProtectRequest 要解决的问题,并不是让 WordPress 变得绝对安全,而是:

减少无效扫描进入 PHP
识别高风险请求
自动封禁恶意 IP
保留攻击行为记录
辅助生成 Nginx 加固规则
让站点安全变得可观察、可管理、可复盘

最终,这套体系的分工应该保持清晰:

Nginx 负责挡掉明显不该进入站点的请求;
ProtectRequest 负责识别、封禁和记录动态风险;
WordPress 负责正常业务;
Redis 负责封禁状态和计数;
人工复盘负责持续优化规则。

它也许无法替代成熟安全插件,但基于真实站点日志,为我私人 WordPress 站点建立了一套轻量、可控、可复盘的请求防护体系。希望它的思路也能给同样维护WordPress 站点的人一些参考。