我的博客人气一般,但是垃圾评论却特别之多,严重影响我的心情。自从开博客以来,我一直都是通过 Akismet 插件来发现和过滤垃圾评论,效果非常不错,很少有漏网之鱼。但是前两天在阅读了云淡然同学写的文章wp_create_nonce实现wordpress垃圾评论终极防御之后,我突然萌生出自己写一些阻挡垃圾评论策略的想法。

wordpress-spam

下面我介绍几条本博客现在正在使用的阻挡垃圾评论的策略,几天下来效果还可以接受。每一种策略都不是完美的,所以只能多种方式配合使用。当然,道高一尺,魔高一丈,很多策略都必须要不断地改进与完善,并且最好根据自己的情况适当地调整。当然,如果你不想折腾,还是老老实实地使用 Akismet 插件来防护吧,插件的功能更加完善可靠,本文的方法只适合于不想启用过多插件和喜欢折腾的同学。

.htaccess 文件

首先我们可以借助 .htaccess 文件来阻挡部分恶意的垃圾评论,这一类评论往往是通过脚本或者工具自动提交的。请将以下内容添加到网站根目录的 .htaccess 文件中:

RewriteEngine On
RewriteCond %{REQUEST_METHOD} POST
RewriteCond %{REQUEST_URI} .wp-comments-post\.php$
RewriteCond %{HTTP_REFERER} !.*kodango.com.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^$
RewriteRule (.*) http://%{REMOTE_ADDR}/$ [R=301,L]

上面的几条规则,可以阻挡来源不是你博客,或者 user agent 信息为空的机器人评论。

如果有时候某个 ip 或者网段的垃圾评论特别凶猛,也可以将该 ip 或者网段屏蔽掉。例如:

order allow,deny
Deny from 123.123.123.123
Deny from 156.156.156.0/24
Deny from 189.189.0.0/16
allow from all

不过,这种方法不是非常推荐,因为很多网站并不是 vps,往往没有独立的 ip 地址,容易造成误杀。

评论黑名单设置

WordPress 本身就带有垃圾评论鉴别的功能,可以在后台"设置-讨论"中配置评论审核和黑名单,每一行是一个关键字,如果评论中出现这些关键字,就会加到评论审核队列,或者直接当作垃圾评论处理。可以将一些明显的推销关键字加到评论黑名单中,这样可以阻挡部分营销推广类型的垃圾评论。

我把自己用的评论黑名单保存在 Gist 上,大家可以参考。

识别垃圾评论

观察了一段时间的垃圾评论,也参考了很多网上流传的比较广的做法,我总结了几条有效地防护策略:

Spam
  1. 如果评论包含黑名单中设置的关键字,则直接丢弃这条评论;

    这条策略的前提是黑名单中的关键字是非常明确可靠的,一些模棱两可的关键字请添加到审核列表中。黑名单中的关键字一旦出现,我就认定这条评论是垃圾评论。

  2. 如果评论中包含日文,则直接丢弃这条评论;

    一般我们的博客没有啥国际友人来看,并且还给你留下评论,日本游客就更少了。所以评论中出现日文时,不要手软,直接标记成垃圾评论吧。事实上,我之前的垃圾评论列表中,有50%以上的评论中包含日文。

  3. 如果评论没有头像,且评论内容中不含中文或者评论字数过多,则标记为垃圾评论;

    我一直坚信,有头像的读者是最真诚的,没有头像的读者大多是雁过不留痕,即使留言也不会非常深刻,大多寥寥几语。另外一方面,基本上95%以上的垃圾评论都是不带头像的,而在我这里基本上是100%。所以如果评论者没有头像,在鉴别这条评论是否为垃圾评论时,请更加严格地对待,但也千万不要抱着“宁可错杀一千,也不可放过一个”的想法将没头像的评论全部挡在门外。根据上面的推推断,我在这里加上了是否包含中文,以及字数多少的限制。

  4. 如果评论有头像或者类型是 pingback/trackback,不含中文且字数过多,则标记为垃圾评论;

    流传地很广地一种做法,是将所有不包含中文的评论全部当做垃圾评论,虽然看起来很武断,但是确实最简单高效,前面我也说过,咋博客还真没到走出国门的那一步,不过虽然现实有差距,也可以让我们幻想一下嘛。所以这里我还加上一个评论字数是否过多的判断,如果评论是一长篇洋洋洒洒的英文,不管你信不信,反正我是不信的。

  5. 如果是普通类型的评论,且评论的 URL过长,则标记为垃圾评论;

    我相信大家都会注册一些简洁并且有意义的网站域名,与此相反,垃圾评论留下的 URL 往往没有意义并且很长。

  6. 如果评论类型是 pingback/trackback,且评论的 URL 中 host 部分长度过长,则标为垃圾评论;

    同上,不过因为 pingback/trackback 评论的 URL 是引用你的那篇文章地址,这个地址往往比较长,所以我们这里只管 URL 中的 host 部分。

如果你觉得3、4、5、6中哪一条的识别率很高,达到99%以上,那么也可以将标记为垃圾评论的处理方法,改成直接丢弃该评论,毕竟垃圾评论依然存在数据库中,需要经常清理。

我们将上面梳理的防护策略转换成实际可工作的代码,然后通过 pre_comment_approved 来识别垃圾评论并标记,使用 preprocess_comment 来直接丢弃垃圾评论。

下面是最终的代码:

/*
 * Get rid of spam comments
 */
function dangopress_getridof_spam($commentdata)
{
    extract($commentdata, EXTR_SKIP);
    $nonce = wp_create_nonce($comment_post_ID);

    /* Check whether the user is in the blacklist */
    if (wp_blacklist_check($comment_author, $comment_author_email, $comment_author_url,
            $comment_content, $comment_author_IP, $comment_agent)) {
        $msg = '您发表的评论中包含被禁止的关键字, 请检查后再评论';
        $msg .= ', 点击<a href="' . $_SERVER['HTTP_REFERER'] . '">此处</a>返回';

        wp_die($msg);
    }

    /* Check whether the comment text contains the japanese chars */
    if (preg_match('/[ぁ-ん]+|[ァ-ヴ]+/u', $comment_content)) {
        wp_die('请勿恶意评论');
    }

    return $commentdata;
}
add_action('preprocess_comment', 'dangopress_getridof_spam');

/*
 * Tag spam comments
 */
function dangopress_tag_spam($approved, $commentdata)
{
    extract($commentdata, EXTR_SKIP);

    /* Check whether the comment is pingback or trackback */
    $is_ping = in_array($comment_type, array('pingback', 'trackback'));

    /* Parse the author url domain part */
    $author_domain = parse_url($comment_author_url, PHP_URL_PATH);

    /* For pingback/trackback, check the comment author domain length */
    if ($is_ping && strlen($domain) > 25) {
        return 'spam';
    }

    /* For normal comment, check the comment author url length */
    if (!$is_ping && strlen($comment_author_url) > 25) {
        return 'spam';
    }

    /* Check whether there is any chinese words exists */
    $has_chinese = preg_match('/[\x{4e00}-\x{9fa5}]+/u', $comment_content);

    /* Check the comment chars length */
    $comment_chars = strlen(trim($comment_content));

    if (!$is_ping) { // Normal comment
        /* Check whether the gravatar exists */
        $has_gravatar = dangopress_validate_gravatar($comment_author_email);

        /*
         * For normal comment without a gravatar:
         *
         * Tag it as spam if no chinese words found, or comment characters are 
         * too many. It's more strict than bellow.
         */
        if (!$has_gravatar && (!$has_chinese || $comment_chars > 120)) {
            return 'spam';
        }
    }

    /*
     * Say too many words without any chinese, tag it spam!
     */
    if (!$has_chinese && $comment_chars > 80) {
        return 'spam';
    }

    return $approved;
}
add_filter('pre_comment_approved', 'dangopress_tag_spam', 99, 2);

小伙伴们,赶紧在自己的博客上试试吧~