• WordPress垃圾评论大作战

    自从把博客搬到独立服务器之后,垃圾评论有日益增长的趋势。

    上次集中清理垃圾评论的时间,刚好是6月初的一天。到8月下旬,居然积攒了5000多条垃圾评论!

    WordPress的用户应该都深有感触,这个博客程序还是挺招惹垃圾评论的。因此WordPress自带了一个叫Akismet的插件。

    Akismet插件工作原理是,将垃圾评论通过API上报到云端(想当年,Akismet诞生时云还不叫云,叫远程服务器),然后也会把垃圾评论存到垃圾评论数据库中。

    不过像我这样几个月收几千条垃圾评论的用户来说,存起来显然不是个好办法。那么,怎么解决呢?

    不急不急,解决之前,不如先分析一下问题吧!

    垃圾评论也分三六九等的,定义如下:

    1. 机器人瞎子一样直接向WordPress的评论接口发数据包
    2. 机器人先解析页面,然后构造请求
    3. 机器人解析页面,还模拟浏览器运行脚本,然后构造请求
    4. 人工手打垃圾评论

    分析了几千条垃圾评论,我觉得,像我这种小博客,99.99%都是第一、第二种情况。第三种几乎没有。第四种的话……也太看得起本站了。

    分析之后,豁然开朗,针对第一种的情况,其实只需要在页面加一些隐含的字段就可以了。不过现在的机器人似乎比较聪明,还是会看一下评论表单的隐藏字段。那么就用脚本再加另外一个隐含字段——只有运行脚本的浏览器,才能正确解析,也才能正确发送这一个由脚本填充的隐藏字段。

    但是,这种方案也有缺点,如果用户的浏览器不支持脚本,也就无法正确发送脚本填充的隐藏字段,同样会被拦截。关键在于,这样的用户有多少呢?

    这时候,大数据(误)就派上用场了。我看了一下来访用户统计,大部分都是chrome或chrome核心,其它移动端的用户也是智能设备。也就是说,感谢科技发展,大部分用户的浏览器都是支持脚本的。那么,这个措施还是可以实施的。

    于是乎,总结上面啰啰嗦嗦的一大串文字结论如下:

    1. 使用脚本在评论表单添加隐藏字段,发表评论时验证字段。
    2. 其余漏网之鱼,交给Akismet拦截。

    参考代码如下:

    <?php

    defined( 'ABSPATH' ) or die( 'No script kiddies please!' );

    define('DEFAULT_JQUERY_BEFORE', 'jQuery(function($) {');
    define('DEFAULT_JQUERY_AFTER', '});');

    function creke_cal_comment_val($k)
    {
    return strval(crc32('CrekeComment'.$k));
    }

    function creke_get_comment_form()
    {
    if(is_user_logged_in())
    {
    return;
    }

    $k = time(NULL);
    $v = creke_cal_comment_val($k);
    return "
    <input type='hidden' name='creke_comment_k' value='$k' />
    <input type='hidden' name='creke_comment_v' id='creke_comment_v' creke_comment_v='$v' value='$k'/>
    ";
    }

    function creke_get_comment_script($jQueryBefore=DEFAULT_JQUERY_BEFORE, $jQueryAfter=DEFAULT_JQUERY_AFTER)
    {
    if(is_user_logged_in())
    {
    return;
    }

    return "
    <script type='text/javascript'>
    $jQueryBefore
    var id='#creke_';
    id+='comment_v';
    $(id).val($(id).attr('creke_comment_v'));
    $jQueryAfter
    </script>
    ";
    }

    function creke_comment_form()
    {
    echo creke_get_comment_form();
    echo creke_get_comment_script();
    }

    add_action('comment_form', 'creke_comment_form');

    function creke_comment_check($commentdata)
    {
    $dieMsg = 'do not spam';
    $dieTitle = 'title';
    $isSpam = FALSE;

    $type = comment_type('comment', 'trackback', 'pingback');

    if(is_user_logged_in() || $type == 'pingback' || $type == 'trackback')
    {
    return $commentdata;
    }

    $k = $_POST['creke_comment_k'];
    $v = $_POST['creke_comment_v'];
    if((time(NULL) - $k) > 86400)
    {
    $isSpam = true;
    }
    if(!$isSpam)
    {
    $isSpam = (creke_cal_comment_val($k) != $v);
    }

    if($isSpam)
    {
    wp_die($dieMsg, $dieTitle);
    }

    return $commentdata;
    }

    if(!is_admin()) {
    add_filter('preprocess_comment', 'creke_comment_check', 1);
    }

    ?>