8.8.13

On the subject of CAPTCHAs

I have a bit of a fascination with CAPTCHAs. And by fascination I mean fear. Mostly because I fail them. Over and over again. In fact, I suck at them so bad that if empirical evidence didn't suggest otherwise, I'd be considered an automaton. After all, that's all these pesky rectangular boxes are supposed to test for - whether the inputer is human or not. Nothing more, nothing less. Somewhere along the way, however, CAPTCHAs have extended their function to include everything from testing one's ability to extract individual letters or words from a lump of skewed characters to testing one's ability to remain calm under severe stress caused by one's inability to extract individual letters from a lump of skewed characters.

Volumes have been written on the pain induced by this lump of skewed characters and its tendency to get skewier and skewier each year. Even when I first started exploring CAPTCHAs, there were hopes and dreams of sexier ways to whip out the Turing test on innocent users that will require them to complete less painful tasks in order to prove their humanity. Like for example, by rearranging an image, or by rotating an image to match the rotation of a set image, or some other much simpler task than extracting individual letters from a lump of skewed characters. There were rumours how Google would switch to such slicker CAPTCHAs entirely. That was many years ago.

Meanwhile I created my first CAPTCHA with PHP's GD. Well, I first created it with pens and pencils on a piece of paper as a part of a blog entry for one of my favourite comp sci classes at the UofA.

Every class I got an A in was a favourite.  

It is still just an image with a bunch of characters in it. It is still painful to look at. But this is programming for programming's sake.

Instead of skewing the characters in the string and glueing them closer and closer together, I just repeated the string underneath the original string so that the bottom half of the first string overlaps the top half of the second string. Also, both strings are slightly transparent so that their overlaps are the most saturated areas in the entire CAPTCHA, thus the rest becomes noise to bots. It is banking on a typical human trait - ability to differentiate between different hues instantaneously. To help humans differentiate the characters, each string is in a different hue.

Most CAPTCHA scanners are not equipped to deal with these sorts of things, I assume.

 my CAPTCHA
 my CAPTCHA scanned







Presumably the bot does not have a built in hue differentiation so the darkest spots on the image, which it presumably interprets as being part of the string are the spots where the two strings intersect, and since these points of intersection do not represent actual letters, the scanner gets nada. Presumably. Personally, I know this CAPTCHA is good because I fail it over and over again. *code is at the end

But let's face it, it still sucks. Humans shouldn't have to suffer while proving their humanity to machines. Instead, they should get some satisfaction from it, even if it is a very small one. Like for example, the satisfaction you get from throwing a ball and getting it through some obstacle.

Instead of a randomly generated string of characters that a person has to reenter, the CAPTCHA could be generating random points in space, drawing a line between them and having the user bounce a ball off of this particular line, or land the ball at a particular location, that's generated dynamically as soon as the user grabs the ball.

Something like this, for example, which uses canvas instead of DOM elements. If you're viewing this on Chrome, just grab the ball and see how much fun it is to throw it around. Keep in mind that this is still a very rough draft and while it's remotely entertaining in its current state, it is not particularly smart and it does no do any checks.




If you can read this, your browser doesnt support this type of stuff.


Below is the code for the first CAPTCHA. The second one is just a bouncing ball drawn on HTML5 canvas.
 <?php   
/* ------------------------------------------------------------------------
 This content is released under the MIT License
 http://www.opensource.org/licenses/mit-license.php
 Author: Marina Ibrishimova (http://ibrius.net)
 Version: 1.0
------------------------------------------------------------------------- */ 
/*--- Note that this is just generating the image captcha  
 ----- You would need to do the checks yourself ------*/  
 //text looks better on png images  
 $im = imagecreatefrompng("path_to_a_blank_box.png");  
 if($im == false) die("Uh oh, hotdog.");  
 //Select Font  
 $font = "path_to_font";  
 //Generate the random string  
 $chars = array("a","A","b","B","c","C","d","D","e","E","f","F","g",  
 "G","h","H","i","I","j","J","k",  
 "K","l","L","m","M","n","N","o","O","p","P","q","Q",  
 "r","R","s","S","t","T","u","U","v",  
 "V","w","W","x","X","y","Y","z","Z","1","2","3","4",  
 "5","6","7","8","9");  
 $length = 8;  
 $code_string = "";  
 for ($i=0; $i<$length; $i++) {  
   $code_string .= $chars[rand(0, count($chars)-1)];  
 }  
 //Create random size, angle, color, and shadow  
 $size = rand(40, 54);  
 $angle = rand(-5, 5);  
 $color = imagecolorallocatealpha($im, rand(0, 100), rand(0, 100), rand(0, 100), 75);  
 $shadow = imagecolorallocatealpha($im, rand(10, 100), rand(10, 100), rand(10, 100), 85);  
 //Determine text size, and use dimensions to generate x & y coordinates  
 $textsize = imagettfbbox($size, $angle, $font, $code_string);  
 $twidth = abs($textsize[2]-$textsize[0]);  
 $theight = abs($textsize[5]-$textsize[3]);  
 $x = (imagesx($im)/2)-($twidth/2)+(rand(-20, 30));  
 $y = (imagesy($im))-($theight/2);   
 $shadowy=$y;  
 $shadowy += 5;  
 $shadowx=$x;  
 $shadowx += 10;  
 //Add text to image  
 imagettftext($im, $size, $angle, $x, $y, $color, $font, $code_string);  
 //Add shadow to the text  
 imagettftext($im, $size, $angle, $shadowx, $shadowy, $shadow, $font, $code_string);  
 header("Content-Type: image/png");  
 imagepng($im);  
 //Destroy the image to free memory  
 imagedestroy($im);  
 ?>