丁宇 | DING Yu

有效地阻止SPAM

对于blog作者来说,SPAM一直是个令人比较头疼的问题。如果没有良好的防治机制,一旦被SPAM机器人盯上,那可不是闹着玩的。目前主要的解决方案是使用Captcha,让真正的留言者按照图片所示,输入一串随机生成的字符串,或是解答数学题等等。虽然攻击者仍可以写程序做图像识别,但这个难度很大,而且一旦图案有变化程序就要重写。因此CA..可以说是目前最为有效的SPAM预防方案。

但是Captcha显而易见的缺点就是:它的用户体验不好,有时甚至很糟。我见过的最恐怖的Captcha来自RapidShare,它居然要你识别一只特定形象的猫!我每次做完这步眼睛都花了!

还有一种使用也比较广泛的方法,就是利用贝叶斯等算法做SPAM识别,有些还结合了可定期更新的黑名单机制。Gmail这方面做得非常好-99%的垃圾邮件都被系统识别并自动归类了。问题是,我始终担心会有重要邮件(比如MM的求爱信……)被误判,还要时不时的去“垃圾箱”中看看。

此外也有人借助Javascript来防SPAM,因为机器人抓取网页时肯定不能运行Javascript,所以可以利用这个特点,通过浏览器事件来辨别留言者的身份。这个方法在Javascript被用户禁用时显然会失效,考虑到Firefox用户可以非常方便地禁用Javascript,我对其持保留态度。

说了半天别人的方法,我介绍一下在我的blog程序Lonely Thinker中的做法。经过半年多的检验,这一方法被证明不仅可以有效地阻止SPAM,又几乎不损害用户体验。

步骤如下:

1、在表单中添加一个值为空、名称为antispam的文本域,将其放在一个HTML容器内(比如div),并用CSS这个HTML容器隐藏(如下图):

view中的代码

2、在表单校验的部分加入对这个文本域的校验,如果其值不为空,则说明当前留言是SPAM(以CakePHP框架的model为例,代码如下):

model中的代码

简单地说,这种方法就是利用了机器人会填写所有表单域的特点而起作用的。唯一可能需要担心的,就是如何处理关闭了CSS的情况。我的做法是在表单中添加一段文字说明,你仔细看上图的话不难发现。

要特别说明一下为什么不直接把文本域用CSS设置为隐藏的。我本来是这样做的,可是发现机器人非常聪明,它似乎有一个简单的解析器,可以读取文本域的CSS,如果发现具有隐藏属性则跳过。前一阵子产生的SPAM都是这么来的,我找到原因时也非常惊讶。

当然了,机器人的作者仍然可以进一步完善他的程序,比如写一个比较完整的HTML解析器,用以对付我目前采用的隐藏HTML容器的做法,但这个有一定的难度,并且我只要让我的HTML不合法(比如没有完整闭合,或者制造标签间的嵌套错误),他的解析器很可能就失效了。

如果你的blog访问量非常大,已经到了有人专门针对你的blog写SPAM机器人的程度,上面的方法就需要改进才行了。我目前想到的一个办法是动态地调整文本域的名称,比如在一个名称池中随机的选取一个渲染到表单里,并在model验证规则的部分做动态调整。为了防止误伤那些在名称调整前载入页面、名称调整后递交表单的用户,甚至可以考虑临时记录每个人载入的文本域的名称。


  1. Felix @ 2008-06-23 19:42:12 +0800:

    在我刚看完《海边的卡夫卡》的时候,“的确是干过猫的事情”这句话真是让我感到有些机缘巧合,呵呵。
    不过我奇怪的就是,为什么每次去RapidShare都遇到happy hour?难道是RP爆发?

  2. taine @ 2008-06-18 17:20:20 +0800:

    RapidShare,哈哈哈哈,是的是的,的确是干过猫的事情。不过现在人家不是给happy hour了嘛~~~

  3. Fenng @ 2008-07-03 00:15:03 +0800:

    这个同学其实更适合设计应用程序 :)

  4. jacob @ 2009-02-02 19:12:47 +0800:

    想没想过用flash做?

  5. 丁宇 @ 2009-02-02 19:22:00 +0800:

    @ jacob 现在我有了更棒的方法!详见:
    http://dingyu.me/blog/posts/view/lonely-thinker-04-and-05-preview
    http://dingyu.me/blog/posts/view/spam-bayesian-chinese-1
    http://dingyu.me/blog/changelog#0.4.4.20090124
    当然,Flash的方法是?