How to implement CAPTCHAs without images

I’ve started getting a lot of spam comments, so I decided the time has come to put a simple system in place to foil the spam robots. In an earlier article I asserted CAPTCHAs are terrible and there are easy ways to foil naive robots without making the site inaccessible or unusable. I’ve implemented a simple question-and-answer system to prove my point. Comment forms show a randomly chosen predefined question (I’ve only put three in the system) and display several predefined answers, only one of which is correct. This took about 30 minutes and maybe 20 lines of actual code to do. Right now the questions and answers are hard-coded in a new include file, but it would be trivial to database them too.

How I did it

  1. Create a new file, say captchas.php
  2. Fill it with an array of entries, one per question:
    <?php
    $captchas = Array();
    
    # Create a single entry
    $captchas[] = array(
        "question" => "What color is the sky?",
        "answer" => "blue",
        "options" => array("blue", "red", "orange"));
    
    # Create as many as desired by copy-and-paste...
    ?>
  3. Modify the comment form. Include the above file in wp-content/themes/default/comments.php, and change a few lines where the form is displayed. At the top of the file,
    <?php require_once("captchas.php"); ?>
    Then, just before the SUBMIT button for the form,
    <?php
    $tabindex = 5;
    $captcha_index = rand(0, count($captchas) - 1);
    ?>
    <input type="hidden"
        name="captcha_index" value="<?php echo $captcha_index; ?>" />
    <p><?php echo $captchas[$captcha_index]["question"]; ?>
    <?php foreach ($captchas[$captcha_index]["options"] as $captcha_answer) { ?>
    <br /><label for="captcha_<?php echo $captcha_answer; ?>">
    <input tabindex="<?php echo $tabindex++; ?>"
        id="captcha_<?php echo $captcha_answer; ?>" type="radio"
        name="captcha" value="<?php echo $captcha_answer; ?>"
        /><?php echo $captcha_answer; ?></label>
    <?php } ?></p>
  4. Now the randomly chosen question’s ID and the user’s answer are submitted along with the form.
  5. On the receiving end of the form, which is in wp-comments-post.php, all I have to do is check the answer against the correct answer for the question. First, I include the captchas.php file as before. Then I grab the two new inputs where the rest of the input is grabbed:
    $comment_author       = trim($_POST['author']);
    $comment_author_email = trim($_POST['email']);
    $comment_author_url   = trim($_POST['url']);
    $comment_content      = trim($_POST['comment']);
    $comment_captcha_idx  = trim($_POST['captcha_index']);
    $comment_captcha      = trim($_POST['captcha']);
    Only the last two lines are changed in that code sample — I included the first lines for context. I use the input a bit later, where the input checking occurs:
    if ( !is_numeric($comment_captcha_idx) || !$comment_captcha
        || $captchas[$comment_captcha_idx]["answer"] != $comment_captcha)
    {
            die( __("Error: wrong answer to the CAPTCHA question"));
    }
  6. That’s it. I’m done. It took longer to explain than to actually write the code.

How hard is it to circumvent this?

If you’re a human, or if you’re trying to bypass just my site, it would be easy to do, but if you’re a spam robot, bypassing the system means learning something about my site (the questions and answers) that is different from other sites. I’m counting on spam robots to be dumber than that, and assume all WordPress sites are the same. Time will tell if I’m right; I anticipate not getting any more spam comments, though. I think the crucial thing is to get it “right enough” that humans find it easy and it’s just a little too hard for spam robots to make it worth anyone’s while. This is, in my opinion, the essential trade-off in security and any other application where you want to stop the Wrong Thing from happening. You can never truly guarantee the Wrong Thing won’t happen, but you can make yourself a less attractive target so all the easy targets will get the heat instead.

Technorati Tags:No Tags

You might also like:

  1. CAPTCHAs without images, part 2
  2. My unorthodox CAPTCHA blocked thousands of spam comments every week
  3. Why CAPTCHAs don’t work well

19 Responses to “How to implement CAPTCHAs without images”


  1. 1 Tim McCormack

    Excellent idea. What’s nice is that since spamming only makes money on volume, spambots won’t be reprogrammed to deal with this particular technique. It’s not worth it to them to spend a little more time on a small number of sites when there are so many sites with absolutely zero protection.

    Incidentally, this also applies to email address mungeing. I use the character literal for the @ sign instead of the actual @ sign in email addresses that have to be openly displayed, and I’ve never received any spam on them. This applies to both the href and the plain text — just use &#064;, and you’re golden. So few people do this that the spambots don’t check for it, and the mailto links still work.

  2. 2 Xaprb

    I was playing around with the character code for the @ sign just last week and wondering how good the compatibility is. You read my mind — spooky! Up till now I’ve always used a script to do an inline document.write() but clearly there are better ways to do it.

  3. 3 Tony

    Nice!

  4. 4 Tim McCormack

    I just hardcode the at and period symbols as characters 64 and 46, respectively. I don’t think any browsers have a problem with at-sign-encoded mailto links, but I don’t know about screen readers.

  5. 5 zhur

    This protection can be easly avoided. Post question to google and calculate frequency of suggested answers. The most frequent word is correct answer.

  6. 6 Xaprb

    That is not always true, if the questions are chosen carefully. See my follow-up to this article. For example, “a lion is a… cat? tree?” Try that on Google. Google returns more results for lion/tree than lion/cat.

    Granted, I have not chosen all my questions carefully. But so far, this system is blocking the vast majority of the hundreds of spam comments I used to get every day… and now I have far more traffic than I did then, so I assume I would be getting thousands a day at this point, if not for this system.

  7. 7 Soldat

    Very nice article! www.php.net use captchas without images too (try to add note there), so it’s professional way to protect web forms. Thank you!

  8. 8 David Nash

    Hi,

    I’m suffering from SPAM comments on a site I built in PHP and have tried several solutions but still i am getting hit with SPAMS. I would like to try your solution but I am a bit confused about the coding being integrated into an existing form.

    could you please provide an example of the code in a complete page so i can see the full correct structure to work out how to modify my page?

    cheers

  9. 9 Trois

    Could you provide this solution for non WP forms as well? I’m trying to implement it in my enquiry form (which uses FormMail to send me the entered data), but got stuck on connecting the form to the comments.php file.

    Thank you in advance.

  10. 10 Xaprb

    Sorry, I won’t have time to do that; not for free at least. You could pay me :-)

  11. 11 EvilSpammer :-)

    It takes less than 3 minutes to get your list and answers (press refresh in the browser)

    … and 5 more minutes to write a perl script wich fills your page….

  12. 12 Xaprb

    In theory, that means lots of people should be spamming me hundreds of times per day. In practice, it’s not happening.

  13. 13 chels75

    nice, it’s better than images ;-)

  14. 14 Web Mechanic

    I just finished installing a CAPTCHA image for a client getting hit by a spam bot. This is an easier solution to implement, especially for those who do not / cannot do any back-end programming. Add to that some bots now have OCR technology embedded in them, so the images are going to get stranger still.

    This solution can be expanded and adapted. Nice. I’m going to play with it - maybe someone can pay me too :)

  15. 15 Anonymous

    From a statistical standpoint, if you are providing the answers as selectable options (i.e. easily parseable from the question), that would be a bad implementation.

    If you have (say) 6 answers to every question, and an infinite number of questions, they could just spam you 6 times, and one would get through, and we know 1 attempt doesn’t cost much.

    Certainly the typing/grammar aspect of it adds security.

  16. 16 HAter of

    One the easiest spam preventions is just to not allow people to insert html tags into the comment. I find 90% of all form spam has some kind of href in it, and I just /dev/null that stuff

  1. 1 Free Mac Software Blog » Blog Archiv » Ein Versuch den Spam einzudämmen
  2. 2 SSDD Web Design » Article » CAPTCHAs done better
  3. 3 My unorthodox CAPTCHA blocked thousands of spam comments every week at Xaprb

Leave a Reply

Please do not use this blog to get help with problems or bugs in Maatkit or innotop: use the appropriate forums, mailing list, or bug trackers. If you're asking for help with MySQL, please use the MySQL mailing list instead.