Block comment spam with bcSpamBlock

Posted on October 10th, 2007 in General,Linux System Administration,Programming by Brandon

A while ago I installed Paul Butler’s JSSpamBlock on my WordPress blog here. His original idea is simple and brilliant: Spambots don’t (yet) execute Javascript. In fact, they usually post directly to the form without even displaying the form first. By having a hidden input field that is populated by javascript, you can verify that users are hitting the page without the user even noticing. For users with JavaScript disabled (are there any of you out there), they simply have to copy/paste a small string into a textbox for verification.

Since implementing a slightly modified version of it on this blog, I have gotten zero spam posts. Now, I wanted some way to implement the same logic on some of my own custom PHP sites to prevent spam on them as well.

While working on a way to re-implement Paul’s WordPress plugin in my own sites, I came up with something pretty clever. Instead of saving a row to a database every time that the form is displayed, you can use a little cryptography to make the client pass all of the data needed to validate the request back to you on its own. The idea is sortof merger between the JSSpamBlock plugin and TCP Syncookies, which use a similar method of having the client store the data for you.

Essentially, how it works, is that the function generates a Random ID. It then encrypts the current timestamp and the random ID using PHP’s crypt() function with some cryptographic salt that is unique to each server. All three of those values (the random ID, the timestamp, and the encrypted value) are then passed to the browser. The timestamp and the encrypted value are stored in hidden <input> fields, while the random ID displayed for the user to verification. If the user has JavaScript enabled, a few lines of JavaScript copy the random ID into another textbox, and then hide that prompt, so that it is never seen by the user. If the user doesn’t have JavaScript enabled, the would have to copy/paste that random ID into the textbox themselves, similar to a captcha.

When the form is submitted, it checks to make sure that the timestamp is not too old, and then re-encrypts the passed in timestamp and random ID using the same salt value to make sure it matches the crypted value passed in from the form. If everything matches, the comment is approved, otherwise an error is displayed to the user.

I wrote this up into a simple include file that can be used for any PHP application. I also implemented a quick WordPress plugin that uses the generic version. More information about it can be found on my new bcSpamBlock home page

25 Responses to 'Block comment spam with bcSpamBlock'

Subscribe to comments with RSS or TrackBack to 'Block comment spam with bcSpamBlock'.


  1. on October 11th, 2007 at 9:07 am

    Cool. I have blogged about it.

  2. Kirk M said,

    on October 28th, 2007 at 11:05 am

    Just so you know, although bcSpamBlock works effectively at blocking spam comments I realized today that it’s been blocking every trackback that’s been sent to my various posts. I found out about this when incoming links were shown in my WP 2.3.1 dashboard from both Google’s blog search and Technorati.

    I tested thoroughly using my WordPress.com blog to send trackbacks to my custom install WP blog and with bcSpamBlocker activated, the trackbacks never showed up. With it deactivated all trackbacks came through okay.

    Thought you might want to know.

  3. wlx said,

    on November 2nd, 2007 at 4:03 am

    I hope the trackback could be solved ASAP.

  4. wlx said,

    on November 2nd, 2007 at 11:08 pm

    just modify with a add_filter to avoid trackback/pingback block:

    function bc_checkfilter($content)
    {
    //not deal with trackback or pingback
    if ( $content['comment_type'] == ‘trackback’ || $content[‘comment_type’$
    if (bcspamblock_verify()) return $content;
    die(‘Your trackback has been rejected.’);
    }
    // Actions
    if(function_exists(‘add_action’)){
    add_action(‘comment_form’, ‘bcspamblock_doform’);
    //add_action(‘comment_post’, ‘bcspamblock_checkcomment’);
    }
    if(function_exists(‘add_filter’)){
    add_filter(‘preprocess_comment’,'bc_checkfilter’,2,1);
    }

  5. wlx said,

    on November 2nd, 2007 at 11:09 pm

    sorry, mis-copyinig.

    function bc_checkfilter($content)
    {
    //not deal with trackback or pingback
    if ( $content['comment_type'] == ‘trackback’ || $content['comment_type']==’pingback’) return $content;
    if (bcspamblock_verify()) return $content;
    die(‘Your trackback has been rejected.’);
    }
    // Actions
    if(function_exists(‘add_action’)){
    add_action(‘comment_form’, ‘bcspamblock_doform’);
    //add_action(‘comment_post’, ‘bcspamblock_checkcomment’);
    }
    if(function_exists(‘add_filter’)){
    add_filter(‘preprocess_comment’,'bc_checkfilter’,2,1);
    }

  6. wlx said,

    on November 3rd, 2007 at 3:07 am

    I make a version that can avoid to block pingback/trackback in my blog: http://wlx.westgis.ac.cn/455/

  7. Brandon said,

    on November 3rd, 2007 at 6:47 pm

    Thanks wlx. I have made those same changes on my own blog here and test for a day or two before committing the changes to the official wordpress plugin.

  8. Kirk M said,

    on November 11th, 2007 at 11:50 am

    Sorry for the delay but I was down and out.

    Thanks for the fix and the update. The plugin was sorely missed. :D

  9. lt said,

    on December 20th, 2007 at 1:51 pm

    hi,

    thanks, works great.

    Im using it in a custom php script.

    however, the comment in line 37 should be:
    if(! bcspamblock_verify()) {

    not:
    if(! bcspamblock_validate()) {

    regards
    lt

  10. lt said,

    on December 20th, 2007 at 1:51 pm

    hi,

    thanks, works great.

    Im using it in a custom php script.

    however, line 37 should be:
    if(! bcspamblock_verify()) {

    not:
    if(! bcspamblock_validate()) {

    regards
    lt

  11. jontiw said,

    on March 26th, 2008 at 9:41 pm

    This is a great approach in principle, and should be sufficient to block any automated bot. But, there appears to be a vulnerability in that the salt value is actually appended to the front of the bcspamblock_hidden field. On the demo page it’s $1$77dbddc7$

    This means that a malicious user could theoretically generate a new bcspamblock_hidden field using the current time, and therefore avoid being timed out.

    Before sending the bcspamblock_hidden field to the output page, remove the seed from the front of it

    substr(bcspamblock_hidden,CRYPT_SALT_LENGTH)

    and then compare only the appropriate characters in the validate function.


  12. on July 2nd, 2008 at 1:27 am

    use bcspamblock in zend form…

    I want to integrate the bcspamblock and zend form, so first to do:
    为了能自动防止机器人攻击,打算把bcspamblock整合到zend_form中。
    1 创建element_bcSpamBlock.php文件,
    <?php
    class element_bcSpamBlock extends Zend_Form_Element…

  13. db0 said,

    on October 16th, 2008 at 12:46 pm

    Just to let you know that I’ve discovered a problem. In version 1.3 bcspamblock stops any trackbacks and pingbacks from reaching the site.. I’ve now tested this on three of my sites.
    All of them are hosted with Dreamhost so perhaps this is related to their setup in case you haven’t heard anyone else having the same issue.

    For now necessarily I’ve disabled the plugin

  14. Siegfried said,

    on December 19th, 2008 at 9:27 pm

    I’ve noticed different behaviour since wordpress 2.7 update – even if user is logged (as admin), sometimes comments are held for moderation. a bit annoying


  15. on December 20th, 2008 at 7:47 am

    Any word on 2.7 support? Anyone tried?

  16. Brandon said,

    on December 20th, 2008 at 11:39 am

    I’ve just upgraded this blog to 2.7 and will see what is going on.

    Thanks,
    Brandon


  17. on December 30th, 2008 at 8:34 am

    Testing to see if it works on 2.7

  18. Siegfried said,

    on January 12th, 2009 at 9:27 am

    any conclusions? may it be browser-issue?
    I am using opera

  19. Brandon said,

    on January 13th, 2009 at 12:15 am

    I did notice a problem when adding/editing comments from the admin screens in WP 2.7. I’ve changed the code to automatically approve comments if logged in to wordpress as an administrator. I have just committed a new version to the wordpress plugin repository.

  20. Alia said,

    on January 13th, 2009 at 1:26 am

    Thanks, this is great!!

  21. Siegfried said,

    on January 13th, 2009 at 8:13 am

    sometimes comments from regular users are held for moderation as well (users not logged, no links in comment)

  22. Brandon said,

    on January 13th, 2009 at 9:29 am

    @Siegfried, I do see that behavior with my blog and never noticed that it wasn’t functioning as intended. I don’ t think that bcspamblock is causing it though. The function that checks the validation either returns the comment as-is or dies with an error message. I don’t see how that could cause it to be flagged for moderation.

  23. Jake said,

    on March 29th, 2010 at 4:37 am

    This plugin works fantastically, it does need an update however, I cannot post from the admin screen as I “fail the security check” or whatever. Could you please fix this?


  24. on February 14th, 2012 at 11:14 am

    Im using that plugins it works perfectly!!! Cheers!!!!


  25. on June 13th, 2012 at 6:41 am

    I had trouble finding a plugin that would work with a theme I recently purchased that had a custom comment form and didn’t use the comment_form() function. I had all but given up. Then I found your plugin, bcSpamBlock. It appears to run flawlessly with my comment form despite not being updated in years, and has stopped SPAM completely (100%) so far. The particular theme I purchased has a thousand or more of purchases and I posted a link to your website/plugin in the support area for this theme. You might notice a big increase in downloads.

    I just want to thank you for this plugin, and to let you know about the possible increase in downloads.

    Thanks!
    Chris

Post a comment

Please copy the string BmkeaC to the field below: