Skip to content

Flickcha - PHP4

We deal with a lot of spam and bots on devbump and after a vulnerability was found in the version of captcha we were using for human verification I had to code a simple math test in its place. That solution is extremely trivial to bypass and so I started looking at alternatives. There are some interesting ones that use pictures instread of garbled numbers which I liked but the ones I saw just rotated a fixed number of images (probably easier to bypass than captcha if one tried). The solution I decided to try and code up was one that used pictures grabbed from flickr which will never be seen twice. So here is my attempt in a PHP4 class with example usage. Feel free to point out any glaringly obvious flaws in it that I might have missed. It does take some time to grab the images since it has to hit as many xml feeds as the number of images you use. It is also a good idea to use tag names that wouldn't normally be associated with eachother to avoid ambiguous images.
Example usage:

$flickcha = new Flickcha("kitten,hamster,bird");
if(isset($_POST['submit'])) {
    if($flickcha->validate())
        echo "Welcome human.";
    else
        echo "Bad robot.";
}
else {
    echo("<form action='$PHP_SELF' method='post' name='commentform'><p>");
    $flickcha->formPrint('commentform');
    echo("<p><input name='submit' type='submit' id='submit' value='Submit' /></form>");
}

Click in order: kitten, hamster, bird

/**
 *  Flickcha class, for human verification
 *  Author: Ian Marsh
 *  Version 0.1
 **/

class Flickcha {
    var $tags;    // tags for photos used in verification
   
    /**
     * Constructor: Contructs a new Flickcha
     **/

    function Flickcha($pass = "puppy,kitten,fish") {
        $this->tags = explode(",", $pass);
    }
   
    /**
     * Validate: Return true if code was corrent, false if not
     **/

    function validate() {
        if(md5($_POST['flickcha']) == $_POST['flickchaKey'])
            return true;
        else
            return false;
    }
   
    /**
     * formPrint: Prints necessary code for html form.
     **/

     function formPrint($formID) {
        // Print instructions
        echo("<div id='flickchaInstructions'>Click in order: ");
        $len = count($this->tags);
        for($i = 0; $i <$len; $i++)
            echo($this->tags[$i].($i == $len-1 ?'':', '));
        echo("</div>")
    
        // Print out flickr photos in random order
        $randTags = $this->tags;
        shuffle($randTags);
        for($i = 0; $i <$len; $i++) {
            $rtag = $randTags[$i];
            echo("<div style='display:inline' class='flickchaThumb' id='thumb$i'>");
            echo("<img class='flickchaImg' src='".$this->getThumb($rtag)."' ");
            echo("onClick=\"document.$formID.flickcha.value+='$i';document.getElementById('thumb$i').style.display='none';\" /></div>");
        }
       
        // Encode correct code
        $key = '';
        for($i = 0; $i <$len; $i++) {
            $key .= array_search($this->tags[$i], $randTags);
        }
        echo("<input type='hidden' name='flickchaKey' value='".md5($key)."' />");
        echo("<input type='hidden' name='flickcha' value='' />");
     }
    
     /**
      * getThumb: Returns url for a flickr img of a given tag
      **/

     function getThumb($tag, $timeout = 10) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, "http://api.flickr.com/services/feeds/photos_public.gne?tags=".$tag);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        $feed = curl_exec($ch);
        curl_close($ch);
        $a = strpos($feed, "img src=&quot;") + 14;
        $b = strpos($feed, "_m.jpg", $a);
        return substr($feed, $a, $b-$a)."_s.jpg";
     }
}

{ 1 } Comments

  1. Yousuf Philips | July 21, 2008 at 12:22 am | Permalink

    Just a note, some countries block flickr, like here in the United Arab Emirates (UAE), so it might be a good idea to temporarily locally cache the file from flickr to solve this issue.

Post a Comment

Your email is never published nor shared. Required fields are marked *