数字营销 · Web开发 · 基础设施

如何控制网站垃圾留言

从企业网站垃圾留言的实际问题出发,分析自动化提交程序与正常浏览器访问之间的差异,并通过 Cookie 与页面资源请求实现一种低干扰的垃圾留言控制方案。

个人网站的表单留言功能一般用于与访客交流,有多种成熟的垃圾评论管理方法可供选择,如 reCAPTCHA、Akismet 插件,甚至直接使用第三方评论交流工具。

然而,对于一些企业网站,尤其是产品推广类网站来说,表单通常用于获取客户询价,是访客转化的重要工具,因此网站管理员在处理垃圾留言时通常会非常谨慎。

不幸的是,很多提供留言功能的企业网站每天都会收到大量垃圾留言。一些企业的信息系统中,正常的信息甚至会被淹没在垃圾信息之中,大量精力被浪费在处理这些信息上。

垃圾留言
垃圾留言

这迫使企业不得不采取措施,例如增加验证码、使用关键词屏蔽、网址屏蔽功能、留言评级插件等。

但这里会有几个问题:

一是增加验证码会增加操作复杂度,很多企业不希望给客户制造额外障碍,以免因此劝退潜在客户;

二是网站咨询本身就比较有限,企业又担心关键词屏蔽或者评级插件误伤正常留言,从而遗漏商机。

针对这种情况,我之前的做法是限制重复提交,控制一条信息中链接的数量,同时增加灰名单功能。灰名单中的留言如果包含指定关键词,就直接进入垃圾箱;而留言中包含黑名单关键词,则直接拒绝提交。这样可以屏蔽掉一部分具有明显特征的垃圾留言。

垃圾留言控制后台
垃圾留言控制后台

然而,仍然有不少漏网之鱼。他们要么发送纯垃圾信息,要么发送各种推销信息。

每天分析这些留言、提取新的屏蔽关键词,本身又变成了一件非常耗费时间的事情。如何以较低的成本,更长期地解决这些问题,就成了不得不考虑的事情。

实际上,经过分析可以发现,这些垃圾留言很多都是通过程序自动提交的。一些不良爬虫在抓取网页之后,会分析网页中的表单,然后直接向表单的 action 地址发送数据。因此可以推测,大部分垃圾留言并不是通过正常浏览器行为产生的。

那么,是否可以直接通过 User-Agent 白名单进行屏蔽?

肯定不行。

攻击者如果连 User-Agent 伪装都不会,基本也很难成为真正的问题。

那么,使用浏览器和不使用浏览器,究竟有什么区别?

最主要的区别在于页面资源的处理方式

试想一个访客来到一个包含表单的页面之后,浏览器通常会先加载页面 HTML,然后继续解析并加载页面中的其它资源,例如图片、CSS、JavaScript 等,最后将页面渲染出来。

而很多简单的爬虫并不会完整执行这些操作,它们可能只获取 HTML,然后直接分析其中的表单结构并发送请求。

基于这一点,我们就可以做一些操作。

当浏览器正常解析页面时,通过某个请求给客户端写入一个 Cookie。之后浏览器再次请求其他资源,包括提交表单时,都会自动携带这个 Cookie。

而一个只抓取 HTML、没有进一步加载页面资源的简单程序,通常不会获得这个 Cookie。

于是,我们就可以在表单处理程序中检查这个 Cookie,从而拦截一部分自动提交的垃圾留言。

具体实现方法如下。

页面中增加请求

在页面中增加一段 JavaScript,向后端服务器发送请求。

如果网站使用 CDN,则需要保证这个请求能够正常回源。如果网站本身已经存在其他每次都需要回源的请求,也可以考虑复用。

例如:

<script>
new Image().src = '//example.com/getcaptcha.php';
</script>

这里使用一个图片请求,是因为它不需要在页面中实际显示图片,同时也能够触发一次 HTTP 请求。

后端接受这个请求,并根据情况给客户端设置 Cookie。

如果有需要,还可以加入随机值或者签名,以便进行更复杂的校验。

示例代码:

<?php

header('Cache-Control: private, max-age=3600');
header('Content-Type: image/gif');

if (isset($_COOKIE['domai_captcha'])) {
setcookie(
'domai_captcha',
time(),
time() + 36000,
'/'
);
}

echo base64_decode(
'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
);

?>

这里的示例只是一个最简单的逻辑。实际使用时,通常应该在首次请求时就设置一个随机 Cookie,并在后续提交时进行有效性验证,而不是只依赖 Cookie 是否存在。

在表单处理程序中检查

最后,在表单处理程序中进行检查:

<?php
if (!isset($_COOKIE['domai_captcha'])) {
header('HTTP/1.1 403 Forbidden');
die('禁止提交,非法访问');
}

// 下面是表单处理逻辑

?>

如果没有这个 Cookie,就认为当前请求可能不是经过正常页面访问流程产生的,从而拒绝提交。

当然,这只是一个逻辑示例,实际业务中的处理程序通常会更加复杂。

CMS中的实现

大部分网站都使用 CMS 进行管理,因此直接修改源代码并不是特别优雅。

较好的 CMS 通常会通过 Hook、Filter 或其它接口提供扩展能力。例如 Domai CMS、WordPress 都可以通过类似的方式实现。

例如在 Domai CMS 的 functions.php 中:

<?php

function _modify_feedback($request) {

// 不让没有获取 Cookie 的请求直接提交表单
if (!isset($_COOKIE['domai_captcha'])) {
return new DM_Error(
__('Message Not Sent', 'hitoy'),
__('Unauthorized Request, Please Use Your Browser to Access and Enable Javascript!', 'hitoy')
);
}

// 下面还可以对请求数据进行进一步处理,
// 例如检查前台提交了大量后端未定义的字段等
}

add_filter('pre_submit_feedback', '_modify_feedback');

?>

这样就不需要修改 CMS 本身的核心代码,而是通过接口对留言提交过程进行控制。

如果用户没有启用JavaScript怎么办?

有聪明的读者可能会想到:我一个访客都不想错过,如果访客的浏览器没有启用 JavaScript 怎么办?其实也可以使用 noscript 标签:

<noscript>
<img
width="1"
height="1"
src="//example.com/getcaptcha.php"
alt=""
>
</noscript>

这样,即使 JavaScript 没有执行,也可以通过图片请求完成 Cookie 的初始化。

最终,我们通过这种方式成功减少了大量垃圾留言。按照当时的实际测试,垃圾留言数量减少了 90% 以上。剩余的一小部分,再通过后台 IP 屏蔽、关键词过滤和人工管理等方式处理,整体工作量就会大大减少。

这种方式最大的特点是:对正常访客几乎无感,同时又可以在服务器端增加一道自动化筛选。

当然,上述代码只是为了说明基本逻辑。实际业务实现还需要根据网站环境进行更完善的设计,例如 Cookie 校验、请求频率限制、来源判断以及更严格的服务端验证等。

另外,这种方法并不是万能的。一旦攻击者了解网站的防护逻辑,就可能针对性地模拟正常浏览器行为。因此,它更适合作为多层防护中的一环,而不应该被认为是唯一的反垃圾机制。

评论0

欢迎分享你的看法,也欢迎补充不同的实践经验。