搜索全站
安全防护 13 分钟阅读

WordPress 用户注册设置与垃圾注册风控:关闭、审核与防护方案

后台一天冒出上千个垃圾用户,多半是注册入口没管好。本文讲清 WordPress 注册功能的开启与关闭、默认角色风险、垃圾注册的三种主要来源,给出验证码、邮箱验证、行为风控的完整配置方案与踩坑清单。

U
UFO_PF 2017-01-01 · 阅读 13 分钟
Wo 安全防护
阅读提示
本文约 4,916 字,预计阅读 13 分钟。文中命令与配置均经过实测,可直接参考执行。

引言:用户列表突然多了 3000 个账号

早上打开后台,”用户”菜单旁边显示 3127。这些账号名全是随机字符串,邮箱是 xxx@mail.ru、xxx@outlook.com 之类,角色是”订阅者”,注册时间集中在凌晨两小时。

这就是典型的垃圾注册(spam registration)。它不只是难看:每个账号都往 wp_users 和 wp_usermeta 插行,数据库膨胀;有些垃圾注册是撞库攻击的前奏;还有相当一部分是 SEO 垃圾——注册后自动在个人资料里塞外链,如果你的主题输出作者归档页,等于免费给别人做了外链 farm。

原理分析:垃圾注册从哪来

垃圾注册主要有三个入口,防护方式完全不同:

  1. wp-login.php?action=register:WordPress 自带的注册页。只要”任何人都可注册”开着,这个页面就公开可达。垃圾脚本会直接 POST 到这里。
  2. WooCommerce 的”我的账户”页:WooCommerce 默认在 /my-account/ 开放注册表单,很多人装了 WooCommerce 却不知道这个入口独立于 WordPress 的全局设置。
  3. 主题/插件自带的注册表单:会员插件、论坛插件(bbPress)、评论插件都可能带自己的注册接口,走的是 admin-ajax.php 或 REST API。

还有一个隐蔽入口:/wp-json/wp/v2/users 这个 REST API 端点。旧版本 WordPress 在未授权情况下可以通过它枚举用户名(?per_page=100),虽然不能注册,但会泄露用户名列表给撞库脚本。

保姆级实操

第一步:先决定要不要开放注册

不需要会员功能的站点,最干脆的做法是直接关掉:

设置 → 常规 → 取消勾选”任何人都可注册” → 保存。

如果已经有人在用注册功能,关掉后已注册用户不受影响,只是新用户注册不了。

第二步:检查默认角色

设置 → 常规 → “新用户默认角色”。默认往往是”订阅者”,这个还算安全。但有些站点被人改成”作者”甚至”编辑”——那垃圾注册进来就能直接发文章带外链,必须检查这一项。

wp option get default_role
wp option update default_role subscriber

第三步:关掉不必要的注册入口

WooCommerce:WooCommerce → 设置 → 账户与隐私 → 取消”允许客户在’我的账户’页面创建账户”。如果只要”结账时创建账户”,单独勾那一项。

REST API 用户端点:

add_filter( 'rest_endpoints', function( $endpoints ) {
    if ( isset( $endpoints['/wp/v2/users'] ) ) {
        unset( $endpoints['/wp/v2/users'] );
    }
    if ( isset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] ) ) {
        unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
    }
    return $endpoints;
});

第四步:加验证码

推荐组合:Cloudflare Turnstile(免费、无感、体验好)或 reCAPTCHA v3。

  • 安装插件 “Simple Cloudflare Turnstile”,在设置里填入 Site Key 和 Secret Key(从 Cloudflare 仪表盘 → Turnstile 免费获取);
  • 勾选需要保护的表单:登录、注册、找回密码、评论;
  • 保存后去前台无痕窗口测试一次。

代码方式(不依赖插件,加在主题的注册表单里):

add_action( 'register_form', 'my_turnstile_render' );
function my_turnstile_render() {
    echo '<div class="cf-turnstile" data-sitekey="你的SITE_KEY"></div>';
    wp_enqueue_script( 'turnstile', 'https://challenges.cloudflare.com/turnstile/v0/api.js', array(), null, true );
}

add_filter( 'registration_errors', 'my_turnstile_verify', 10, 3 );
function my_turnstile_verify( $errors, $sanitized_user_login, $user_email ) {
    if ( empty( $_POST['cf-turnstile-response'] ) ) {
        $errors->add( 'turnstile_error', '请完成人机验证' );
        return $errors;
    }
    $resp = wp_remote_post( 'https://challenges.cloudflare.com/turnstile/v0/siteverify', array(
        'body' => array(
            'secret'   => '你的SECRET_KEY',
            'response' => sanitize_text_field( $_POST['cf-turnstile-response'] ),
            'remoteip' => $_SERVER['REMOTE_ADDR'],
        ),
        'timeout' => 10,
    ));
    $body = json_decode( wp_remote_retrieve_body( $resp ), true );
    if ( empty( $body['success'] ) ) {
        $errors->add( 'turnstile_error', '人机验证未通过,请重试' );
    }
    return $errors;
}

第五步:邮箱验证 + 人工审核

单纯验证码挡不住真人注册的垃圾号。会员站建议加”邮箱验证后才能登录”:装插件 “WP Approve User” 或 “New User Approve”,新用户注册后状态置为 pending,管理员在后台逐个批准。

轻量做法是注册后发激活邮件,7 天内未激活自动删除:

-- 清理 7 天前注册且未激活的账号(配合插件 meta 判断)
SELECT u.ID, u.user_login, u.user_email, u.user_registered
FROM wp_users u
LEFT JOIN wp_usermeta m ON m.user_id = u.ID AND m.meta_key = 'pw_user_status'
WHERE u.user_registered < DATE_SUB(NOW(), INTERVAL 7 DAY);

第六步:批量清理已有垃圾账号

特征判断:没有已发布文章、注册时间集中、邮箱域名异常、没有 last_update 记录。

# 列出从未发过文章的订阅者
wp user list --role=subscriber --fields=ID,user_login,user_email,user_registered | head -50

# 批量删除(谨慎!先导出备份)
wp user delete 123 124 125 --yes --reassign=1

--reassign=1 表示把这些用户的内容转移给 ID=1 的用户,不加这个参数内容会被删掉。

更快的方式是 SQL(先备份):

DELETE u, um
FROM wp_users u
LEFT JOIN wp_usermeta um ON um.user_id = u.ID
WHERE u.ID IN (
  SELECT user_id FROM (
    SELECT u2.ID AS user_id
    FROM wp_users u2
    LEFT JOIN wp_posts p ON p.post_author = u2.ID
    WHERE p.ID IS NULL
      AND u2.user_registered < '2026-09-01'
  ) AS t
);

第七步:封 IP 段

如果垃圾注册来自固定几个 IP 段,直接在 Nginx 层挡:

location = /wp-login.php {
    deny 45.155.0.0/16;
    deny 193.32.0.0/16;
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}

更省事的做法是上 Cloudflare,在 WAF 里建规则:(http.request.uri.path contains "wp-login.php" and http.request.method eq "POST" and cf.threat_score > 10) → 阻断。

常见踩坑与失败案例

  1. 案例一:某站关了 WordPress 全局注册,但垃圾用户照涨——因为装了 WooCommerce,/my-account/ 的注册入口是独立的。所有会员/电商/论坛插件都有自己的注册开关,要逐个查。
  2. 案例二:加了 reCAPTCHA 但没生效。原因是主题的注册表单是自定义的 AJAX 表单,压根没走 register_form 钩子,验证码渲染不出来。这种情况要改主题的模板文件手动插入。
  3. 误删真实用户:按”没发过文章”批量删,会把网站会员、评论者一起删掉。删之前一定要先 SELECT 看一遍邮箱域名,有 qq.com、163.com 的一律别删。
  4. 验证码和缓存冲突:页面缓存插件缓存了登录页,导致每个用户拿到的 challenge 是同一个,验证必然失败。要在缓存插件里排除 /wp-login.php 和 /my-account/。
  5. 关掉注册后仍被枚举用户名:加一条 Nginx 规则挡掉 ?author=N:

nginx
if ($args ~* "^author=\d+") { return 403; }

  1. 邮件发不出去导致激活流程断裂:WordPress 默认用 PHP mail(),很多云厂商封了 25 端口。配 SMTP 插件(WP Mail SMTP)接阿里云/腾讯云邮件推送或 SendGrid。

总结

垃圾注册治理的正确顺序是:先确定业务是否需要开放注册(不需要就关)→ 检查默认角色是不是被改成了作者 → 逐个排查 WooCommerce / 会员插件的独立注册开关 → 加 Cloudflare Turnstile → 会员站加邮箱验证和人工审核 → 存量垃圾账号按特征批量清理 → 最后在 CDN / Nginx 层封异常 IP。做完这七步,绝大多数站点的新增垃圾注册能降到零。记住一条:关掉入口永远比事后清理便宜。

U
UFO_PF作者

专注 WordPress 生态的实战作者,内容以「可复制、可验证」为第一原则。

留下你的想法

邮箱地址不会被公开。带 * 的为必填项。