本文目录导读:

开发PHP项目的评论防灌水功能,需要从前端限制、后端验证、行为分析和内容过滤四个层面综合防范,以下是具体的技术方案和代码示例:
前端基础限制(防小白/机器脚本)
这些措施成本低,能过滤掉70%的初级攻击。
提交频率限制(JS侧)
<script>
let lastSubmitTime = 0;
const MIN_INTERVAL = 5000; // 5秒内只能提交一次
document.getElementById('commentForm').addEventListener('submit', function(e) {
const now = Date.now();
if (now - lastSubmitTime < MIN_INTERVAL) {
e.preventDefault();
alert('提交太快了,请稍候再试');
return;
}
lastSubmitTime = now;
});
</script>
必填字段与格式校验
- 验证邮箱格式、URL格式(如果允许)
- 使用HTML5
required属性,配合JS正则:function validateComment(content) { // 禁止纯数字、纯字母、重复字符 if (/^(\d+)$/.test(content)) return false; if (/^(.)\1{5,}$/.test(content)) return false; // 如"哈哈哈哈哈哈" // 最小长度限制 if (content.trim().length < 2) return false; return true; }
后端核心防护(防绕过)
后端是最终的防线,必须对所有输入进行严格检查。
频率限制(Rate Limiting)
基于IP+用户ID(如果登录)的滑动窗口限制:
<?php
class RateLimiter {
private $redis;
private $maxAttempts = 10; // 最大次数
private $windowTime = 3600; // 窗口时间(秒)
public function check($key) {
$current = time();
$windowStart = $current - $this->windowTime;
// 使用Redis有序集合存储时间戳
$this->redis->zRemRangeByScore($key, 0, $windowStart); // 清理过期记录
$count = $this->redis->zCard($key);
if ($count >= $this->maxAttempts) {
return false; // 超过限制
}
$this->redis->zAdd($key, $current, $current . '_' . uniqid());
$this->redis->expire($key, $this->windowTime);
return true;
}
}
// 使用
$limiter = new RateLimiter();
$ipKey = 'comment_limit:' . $_SERVER['REMOTE_ADDR'];
if (!$limiter->check($ipKey)) {
die('评论过于频繁,请一小时后再试');
}
?>
验证码机制(CAPTCHA)
- 简单验证:算术题(3+5=?)、简单图片
- 高级验证:Google reCAPTCHA v3(无感验证)
- 隐藏字段:使用Honeypot技术(对用户不可见,填了就是机器人):
<!-- 对用户隐藏 --> <input type="text" name="website" style="display:none;" autocomplete="off">
后端检查 `$_POST['website']` 如果有值则拒绝。
#### 3. 内容安全过滤
```php
<?php
class CommentFilter {
// 黑名单关键词
private $badWords = ['http://', 'https://', '免费', '加微信', 'Q群'];
// 链接检测
public function hasLinks($content) {
return preg_match('/https?:\/\/[^\s]+/i', $content);
}
// 重复内容检测(同一用户同样内容)
public function isDuplicate($userId, $content) {
global $db;
$stmt = $db->prepare("SELECT COUNT(*) FROM comments
WHERE user_id = ? AND content = ?
AND created_at > DATE_SUB(NOW(), INTERVAL 1 DAY)");
$stmt->execute([$userId, $content]);
return $stmt->fetchColumn() > 0;
}
// 主要过滤逻辑
public function sanitize($content) {
// 1. 去除空白
$content = trim($content);
// 2. 检查长度
if (mb_strlen($content) > 500) return false;
// 3. 检查敏感词
foreach ($this->badWords as $word) {
if (stripos($content, $word) !== false) return false;
}
// 4. 检查链接(非白嫖网站)
if ($this->hasLinks($content) && !$this->isWhitelistedDomain($content)) {
return false;
}
return htmlspecialchars($content, ENT_QUOTES, 'UTF-8'); // XSS防护
}
}
?>
高级行为分析(防高级灌水)
用户行为评分
根据以下维度给用户打分:
- 新用户:注册<7天 → 限制评论数
- 低质用户:之前评论被删除率>30% → 手动审核
- 设备指纹:记录User-Agent、屏幕分辨率、时区(批量机器人往往一致)
时间模式检测
<?php
// 判断是否是机器人的时间规律
$submitTime = time();
if ($this->isBetween($submitTime, '00:00', '06:00')) {
// 深夜提交,触发人工审核标记
$this->markForReview($comment);
}
?>
文本模式分析
- N-gram分析:检测重复短句(如"很好"重复10次)
- 字符分布:检查字母/数字/中文的比例异常(如全英文+数字的垃圾评论)
内容可信度过滤
垃圾邮件检测
使用PHP的 akismet API 或 spamcheck 服务:
<?php
$akismet = new Akismet('your-api-key', 'https://yourdomain.com');
$akismet->setCommentAuthor($name);
$akismet->setCommentContent($content);
$akismet->setUserIP($_SERVER['REMOTE_ADDR']);
if ($akismet->isCommentSpam()) {
// 标记为垃圾评论,不显示
$comment->setStatus('spam');
}
?>
相似度检测重复发布(例如水军复制粘贴):
<?php
// 使用similar_text()检测
function isSimilarToRecent($content, $threshold = 80) {
global $db;
$stmt = $db->query("SELECT content FROM comments
WHERE created_at > NOW() - INTERVAL 1 HOUR
ORDER BY created_at DESC LIMIT 50");
while ($row = $stmt->fetch()) {
similar_text($content, $row['content'], $percent);
if ($percent > $threshold) {
return true;
}
}
return false;
}
?>
完整的服务端处理流程
<?php
// 评论提交接口
public function submitComment($data) {
$userId = $data['user_id'];
$content = $data['content'];
// 1. 频率检测
if (!$this->rateLimitCheck($userId, $_SERVER['REMOTE_ADDR'])) {
return ['success' => false, 'msg' => '操作过于频繁'];
}
// 2. Honeypot检测
if (!empty($data['website'])) {
return ['success' => false, 'msg' => '机器人检测失败'];
}
// 3. 验证码校验
if (!$this->validateCaptcha($data['captcha'])) {
return ['success' => false, 'msg' => '验证码错误'];
}
// 4. 内容过滤
$filteredContent = $this->filter->sanitize($content);
if ($filteredContent === false) {
return ['success' => false, 'msg' => '评论内容含有违规信息'];
}
// 5. 重复检测
if ($this->filter->isDuplicate($userId, $filteredContent)) {
return ['success' => false, 'msg' => '请勿重复提交相同内容'];
}
// 6. 行为分析 - 判断是否标记审核
$status = $this->behaviorAnalyzer->assess($userId);
// 7. 存储到数据库(含审核状态)
$this->saveComment($userId, $filteredContent, $status);
return ['success' => true, 'msg' => '评论提交成功'];
}
?>
推荐组合方案(根据项目规模)
| 项目类型 | 必备措施 | 可选增强 |
|---|---|---|
| 小型个人博客 | 频率限制 + 简单验证码 + 内容过滤 | Honeypot + 人工审核 |
| 中型社区 | + reCAPTCHA v3 + 限频Redis + 文本模式 | 用户行为评分 + 黑白名单 |
| 大型网站 | 全部 + Akismet API + 机器学习判断 | 设备指纹 + 分布式限流 |
重要提醒
- 不要只依赖前端:所有前端验证都可以通过Curl等工具绕过
- 日志记录:记录所有被拒绝的请求(IP、时间、内容),便于后续调整策略
- 用户申诉:为被误判的用户提供申诉渠道(如“需要人工审核”)
- 逐步升级:先从最简单的频率限制+验证码做起,再根据灌水情况增加复杂度
通过以上组合策略,可以有效应对90%以上的灌水攻击,同时保持良好用户体验,如果需要更具体的代码实现(如自定义验证码、Redis限流示例),欢迎进一步沟通。