WordPress 安全防护简单记录

WordPress网站一直是被恶意程序扫描和密码爆破的重灾区, 每次一查Nginx日志都能看到一大堆对本博客的密码撞库攻击和敏感路径扫描
在几年前由于用了弱密码还真被爆破成功, 虽然我很快就改了密码但由于不确定是否有留下暗桩, 索性重装了一遍系统, 都是血泪教训. 随着跑在服务器上的业务越来越多, 重装的代价已经是非常大了. 从那次事故以后我便开始留意WordPress的安全防护, 下面是我这些年来做的配置的一些简单记录.

1. WordPress插件篇

WordPress推荐使用插件 Login Lockdow 的登录验证码功能, 类型直接选择built-in Captcha即刻, 亲测可以抵挡99%的密码撞库程序
可以看到登录失败的全是给验证码拦截了
WordPress上还有很多更加全面的安全插件, 但过多或者过于复杂的插件会减慢站点速度, 特别是对我在用这个小鸡性能很一般, 因此我会更倾向在下面几个方面来做后续的防护.

2.Nginx篇

在Nginx上我主要是对WordPress中的一些敏感路径进行访问限制, 主要配置如下

   #禁止以. 开头的文件访问, 如.htaccess, .htpasswd, .DS_Store (Mac).
    location ~ /\. {
        deny all;
    }

    #禁止访问路径 /xmlrpc.php
    location ~ /xmlrpc.php$ {
        deny all;
    }

    #禁止访问路径 /wp-json/wp/v2/users
    location ~ ^/wp-json/wp/v2/users {
        deny all;
    }

    #禁止访问路径 /wp-includes/wlwmanifest.xml
    location ~ ^/wp-includes/wlwmanifest.xml {
        deny all;
    }

    #禁止直接访问uploads和files文件夹下的php文件
    location ~ /(?:uploads|files)/.*\.php$ {
        deny all;
    }
其中/xmlrpc.php是除了/wp-login.php 之外的另一种可以用来验证后台密码的方式, 因此也经常会被用来做密码撞库;
/wp-json/wp/v2/users 会返回后台管理员的登录用户名, 建议禁止并且管理员登录名不要设置成Admin这种常见的;
/wp-includes/wlwmanifest.xml 是我在别的博客看到的, 但是新版WordPress好像移除了这个文件, 反正我用的版本目录下没有, 关于这个文件的限制可以不写, 最重要的还是前面两个路径.

3.CDN篇

如果你的网站套了CDN的话, 可以通过CDN里提供的一些配置来相关的防护. 不同CDN提供商的功能都有所不同, 这里我主要记录我正在用的两家CDN的配置.

3.1 又拍云

在CDN的访问控制选项下又拍云提供了网站安全防护的基本功能, 我主要用到的是 IP 访问限制 和 WAF保护 这两个. ip访问限制可以对敏感路径设置一个阈值, 如果某个ip在短时间内对该路径的请求超过该阈值, 那么它可能是在做密码撞库, 这时就会禁止他的访问请求. 我的配置如下
我的阈值设置成20次/分, 封禁时间是8400秒, 可以多观察网站的访问日志, 根据自己的情况来建议在上方的ip白名单中加入自己的ip防止误杀. 又拍云也提供了 WAF保护, 但是只有一个功能开关, 不能配置规则, 也没有分析统计, 在这方面不如Cloudflare. 这项配置没什么好说的, 打开就好.

3.2 Cloudflare

相较于又拍云, Cloudflare提供的安全防护就全面了很多. 提供了WAF, DDoS防护, 页面规则等一系列功能, 其中WAF是可以自己写规则的, 可以说是非常丰富了. 我在Cloudflare上的安全配置基本都是在WAF里, 下面是我写的一些规则:

第一条是专门为WordPress写的, 一是阻止WordPress的敏感路径扫描,二是阻止规定以外的ip进行登录请求

(http.host wildcard "merack.top") and ((cf.waf.credential_check.password_leaked) or (http.request.uri.path wildcard r"/wp-login.php" and (ip.geoip.country ne "CN" and (not ip.src in {192.168.1.1}))) or ((http.request.uri.path wildcard r"/xmlrpc.php") or (http.request.uri.path wildcard r"/wp-json/wp/v2/users"))) 

http.host wildcard "merack.top" 表示只匹配对merack.top的访问;

cf.waf.credential_check.password_leaked 表示cf会检测登录请求中的密码是否是已知的通过各种信息泄露事件泄露过的密码, 具体描述可以看Cloudflare官方的这篇文档, 不少自动程序除了尝试弱密码组合之外还会尝试泄露过的数据库中的密码; http.request.uri.path wildcard r"/wp-login.php" 表示匹配访问路径是/wp-login.php的请求;

ip.geoip.country ne "CN" and (not ip.src in {192.168.1.1}) 表示匹配请求ip不是来自中国的并且不在受信任ip里的(这里以192.168.1.1)为例, 建议将正在托管WordPress的服务器ip加入到列表中, 因为通过Nginx日志观察, WordPress会定时请求自身来触发一些定时任务(比如/wp-login.php), 如果你的服务器ip恰好不在中国内又不在信任ip列表中, 那么可能会被拦截, 也可以在这条waf规则前再写一条白名单规则放行; 

(http.request.uri.path wildcard r"/xmlrpc.php") or (http.request.uri.path wildcard r"/wp-json/wp/v2/users") 表示匹配这两个敏感路径的请求, 这两个路径在前面已经做过介绍.

最后将这几个匹配条件按照相应的逻辑通过逻辑and 和 or 连接起来, 大概如下图

效果还是很可观的

第二条规则是对域名下的所有站点都生效的, 主要是防止除了已知的搜索引擎的蜘蛛机器人之外的其他自动程序和高风险IP对站点进行扫描, 这里的逻辑比较简单, 直接用可视化的表达式生成器即可:
与第一条规则不同, 这里没有指定 http.host wildcard "merack.top" , 所以该条规则对域名下的所有子域名都有效.

图中的威胁分数是Cloudflare对ip的恶意程度进行的一个评估, 请求的威胁评分值为0到100, 其中0表示低风险. 大于10的值可能表示垃圾邮件发送者或机器人, 大于40的值表示互联网上的不良行为者, 很少有超过60的值. 详细描述可以参照官方的这个文档.官方推荐设置大于10触发托管质询, 大于50直接block.

下面的选择操作->托管质询 是指Cloudflare会生成一个用于人机验证的验证页面, 通过验证才可以继续访问.

最后的规则总览
执行质询类的操作都会自动计算一个CRS值, 这个值可以体现质询规则的有效性, 越低表示误杀的真实用户越少, 则效果越好.
如果在CSR为0的同时Nginx日志中还有大量的路径扫描请求记录, 那么可以尝试降低威胁分数的值, 也有可能是这些IP未被收录在Cloudflare的数据库中所以不触发质询, 总之这个要慢慢调, 防扫描我还在学习中. 这条规则我也是刚写等待后面的测试.