论坛和博客最怕的一件事,不是没人访问,而是被搜索引擎判定为「垃圾外链牧场」之后,连带主域名一起被降权。尤其是用 Discuz、Xiuno、Flarum 这类开源程序建的站,注册即能发帖、签名档可以放链接、头像还能填外链 URL——这三个入口如果不管,站长睡觉的时候都有人在给你批量灌广告。本文讲的是怎么用一套「注册期 + 内容期 + 行为期」的三层防线,把机器人挡在门外,而不是等广告发满一整版再去删。
先搞清楚:机器人不是靠 IP 封得住的
很多站长的第一反应是「封 IP」。但现实是,代理池成本已经低到可以忽略,一个 Socks5 池几块钱就能换上千个出口。你封掉一个 C 段,对方换一个继续来,而你的防火墙规则越堆越长,最后误伤了真实用户。
真正有效的思路是提高「发帖成本」——让机器人为了发一条广告,必须付出远高于收益的代价。具体可以从四个维度下手:
- 时间成本:注册后不能立刻发帖,必须等够一定时长
- 验证成本:一次性图形验证 + 行为验证,而非纯 IP 判断
- 内容成本:发帖时链接数量、关键词密度受硬性限制
- 账号成本:新账号权重低,发帖需要审核,攒够信誉才放开
下面按「注册期 / 内容期 / 行为期」三层来落地。
第一层:注册期拦截
1. 关闭「注册即发帖」权限
以 Discuz X3.5 为例,后台进入「用户 - 用户组 - 新手上路」,把「允许发帖」关掉,或者设置为「发帖需要审核」。这一步能挡掉绝大多数「注册完立刻刷十帖走人」的脚本。
具体操作路径:
后台 → 用户 → 用户组 → 新手上路 → 编辑 → 帖子相关
├─ 允许发表主题: 是
├─ 发表主题是否需要审核: 是
├─ 允许发表回复: 是
└─ 发表回复是否需要审核: 是
后台 → 全局 → 注册与访问控制
├─ 新用户注册后多少分钟内不允许发帖: 10
└─ 新用户注册后多少小时内发帖需要验证码: 24Discuz 把「发帖间隔」和「注册后冷静期」做成了原生配置,很多站长装完站根本没去看过这一页,默认值往往全是「否」。
2. 启用邮箱域名白名单
一次性邮箱是机器人注册的重灾区。与其维护一个几千条的临时邮箱黑名单(永远更新不完),不如反过来做白名单:只放行主流邮箱域名。
在 Discuz 里可以通过「注册与访问控制 - 禁止的邮箱域名」反向配置,但真正的白名单校验建议在 Nginx 层或者二次开发钩子里做。一个简单的 PHP 校验函数长这样:
function is_allowed_email($email) {
$allowed = ['qq.com','163.com','126.com','gmail.com','outlook.com','foxmail.com'];
$domain = strtolower(substr(strrchr($email, '@'), 1));
return in_array($domain, $allowed, true);
}注意不要放行 .top、.xyz 这类整段后缀——除非你确实面向那类用户。放行后缀等于放弃这道防线。
3. 图形验证码 + 行为验证叠加
纯图形验证码早就能被 OCR 秒破。现在的做法是「图形验证 + 时间戳签名 + 前端行为检测」三件套。Discuz 自带的是老式 GIF 验证码,建议换成基于 Canvas 的滑动验证,或者接入极验、Vaptcha 这类免费额度的服务。
如果不愿意依赖第三方,可以用一个「蜜罐字段」方案,成本几乎为零:
<input type="text" name="website_url" id="website_url"
style="position:absolute;left:-9999px" tabindex="-1" autocomplete="off">再在表单提交处理里判断:如果 website_url 有值,直接判定为机器人。正常用户看不到这个字段,也不会填,而大多数自动化脚本会把页面上所有 input 都填一遍。
第二层:内容期过滤
1. 链接数量硬性限制
这一层最有效。新账号第一条帖子最多允许 0 个外链,攒够一定积分才放宽。以 Discuz 为例,可以在「用户组 - 帖子相关 - 允许使用 [url] 代码」里关闭,而在自定义开发里直接统计正文中的 http:// 和 https:// 出现次数:
// 发帖前置校验:新用户链接数限制
$linkCount = preg_match_all('#https?://#i', $message, $m);
$userLevel = get_user_level($uid);
$limit = ($userLevel === 'newbie') ? 0 : (($userLevel === 'member') ? 2 : 99);
if ($linkCount > $limit) {
showmessage('您的账号等级暂不支持发布外部链接,请先积累一定发帖量。');
}机器人发广告的核心诉求就是放链接。链接放不进去,它对你这站的兴趣立刻归零。
2. 关键词与正则拦截
把敏感词表放在一个独立文件里,方便随时增补,而不是硬编码进代码:
# /www/forum/config/spam_words.txt
代开|发票|赌|博彩|棋牌|私服|外挂|办证|贷款|加微信|加V|日赚|躺赚|刷单$spam = file_get_contents('/www/forum/config/spam_words.txt');
if (preg_match('/' . $spam . '/u', $message)) {
// 命中直接进回收站 + 记录 uid
insert_spam_log($uid, $message);
showmessage('内容包含违规词汇,已拦截。');
}注意中间那条竖线写成了 | 的正则分支,中文词的边界问题要靠 /u 修饰符保证按 UTF-8 字符匹配,否则会把「赌博」切在半个字节上。
3. 针对签名档和头像单独立规矩
这是最容易被忽略的入口。很多站长把正文防得死死的,结果机器人把广告写在签名档里,每条回复底下都挂一个外链,效果反而更好。
处理方式很直接:新用户组禁止使用签名和自定义头像;签名里出现外链直接清空该字段。Discuz 后台路径是「用户组 - 帖子相关 - 允许签名中使用 Discuz! 代码」,关掉即可。
第三层:行为期风控
1. 发帖频率限制
正常人一分钟发不了五条帖子。给每个 uid 做一层 Redis 计数器:
$key = "post_rate:{$uid}";
$count = $redis->incr($key);
if ($count === 1) {
$redis->expire($key, 60); // 60 秒窗口
}
if ($count > 3) {
showmessage('发帖过于频繁,请稍后再试。');
}Redis 的 INCR 加 EXPIRE 是最轻量的滑动窗口实现,单机轻松扛住几万 QPS。如果你的站还没装 Redis,用文件锁也能凑合,只是并发高时会有误差。
2. 注册与首帖的 IP / UA 关联分析
机器人往往在同一分钟内用同一个 UA 注册十几个账号。把注册记录落一张表,定期跑一条 SQL 就能揪出团伙:
SELECT ip, COUNT(*) cnt
FROM pre_common_member
WHERE regdate > UNIX_TIMESTAMP() - 86400
GROUP BY ip
HAVING cnt > 5
ORDER BY cnt DESC;再按 UA 分组看一次,如果某个 UA 的注册量是其他所有 UA 总和的十倍,那基本可以确定是脚本:
SELECT COUNT(DISTINCT ip) AS ips, COUNT(*) AS regs, regip
FROM pre_common_member
GROUP BY regip
HAVING regs > 10;查到之后不要急着删号——先标记,再看这些号有没有发过内容。如果是「注册了但从不说话」的僵尸号,批量清理;如果已经开始发广告,说明对方在养号,这时要考虑直接封整个注册 IP 段。
3. 冷启动账号的发帖排序降权
这一条属于「软防御」。新账号的帖子默认排在列表后面,或者不进首页「最新主题」,让对方即使发成功也拿不到流量。Discuz 可以在模板层按 uid 注册时间排序,也可以给新账号帖子加一个 displayorder 负值偏移。
兜底:定时清理与申诉通道
再严密的风控也有漏网之鱼。建议写一个 cron 脚本,每天凌晨扫描一遍「注册 24 小时内、发帖数 > 5、含外链」的账号,自动转入审核队列:
#!/bin/bash
# /root/forum_spam_clean.sh 每日 03:30 执行
mysql -uroot -pPASSWORD forum_db -e "
SELECT m.uid, m.username, COUNT(t.tid) AS posts
FROM pre_common_member m
JOIN pre_forum_thread t ON t.authorid = m.uid
WHERE m.regdate > UNIX_TIMESTAMP() - 86400
GROUP BY m.uid
HAVING posts > 5;
" >> /var/log/forum_spam.log 2>&1注意 >> 和 2>&1 这两处:前者是追加而不是覆盖,后者保证错误信息也不丢。日志文件记得配 logrotate,否则一年后你会发现它涨到几个 G。
同时一定要留一个申诉入口——误判是必然的,尤其是新站刚上线、真实用户行为也不规范的阶段。给被拦截的注册留一个「为什么我发不了帖」的说明页,比冷冷甩一个 403 要好得多。
小结
三层防线的本质是把「发一条广告」的成本从「几秒钟」抬到「几十分钟甚至几天」。机器人运营者是算投入产出比的,成本一高,他们自然会去挑那些默认配置、三个月不登录的懒站长下手。
落地顺序建议:先把用户组的发帖权限和签名档关掉(10 分钟,零成本),再加上蜜罐字段和链接数限制(半小时),最后才考虑 Redis 频率限制和 IP 关联分析。不要在还没解决基础配置问题的时候,就去上复杂的风控系统。