Click here to Skip to main content
Email Password   helpLost your password?

A brief overview of the most known implementations

Why are the image-based CAPTCHAs NOT so widespread as the text-based ones?

I will not touch on the contrastive analysis of the possibility to crack them, you can find some thoughts/calculations here and here. I want to express my point of view as a web developer. So, why?

I have to note that it is not a criticism in any way - I only want to find an answer for the above question. Let's sum up the aforesaid. An image-based CAPTCHA might be a good alternative to a text-based one if it would be a single, light-weight image based on a limited set of pictures.

The idea

Look at these two pictures:

The original image
The changed image

It is easy to notice that the right image is slightly distorted, and it is not hard to outline a rough region where the distortion takes place. In order to notice it, the original image is not required. A human easy copes with this task even he sees an image the first time and does not know what the image depicts - the aforesaid does not apply to an expressionist's pictures :).

Now about bots. I have never worked with image recognition systems and my knowledge in this area is rather poor. Perhaps, the proposed variant is intricate to parse by special programs, perhaps not - it will be interesting to hear an expert's opinion.

The code overview

The proposed image-based CAPTCHA control works in such a way: a visitor sees a picture with a distorted part and he has to click elsewhere in the anomalous region boundaries. The point he clicks on will be marked with a red spot.

The control fulfils a double functionality, it renders its HTML content and forms the picture itself. It has two child controls: an image, and a hidden field that serves to store coordinates of the visitor's chosen point. The image URL forms by adding the special parameter to the currently requested URL. When the request to this new URL comes, the control interrupts the usual process of page loading and writes the image as a byte array in the response.

protected override void OnInit(EventArgs e)
{
    if (HttpContext.Current.Request[queryKey] != null)
        DrawImage();
        .....
}

private void DrawImage()
{
    Bitmap bitmap;
    
    //the image creation goes here

    ....
    
    HttpContext.Current.Response.Clear();
    HttpContext.Current.Response.ContentType = "image/jpg";
    bitmap.Save(HttpContext.Current.Response.OutputStream, 
                ImageFormat.Jpeg);
    HttpContext.Current.Response.End();
}

This approach allows to compound all the functionality in a single control. In a heavily loaded environment, it is better to take out the image creation elsewhere to another place - for example, to the HttpHandler - to avoid complicating the creation of the page where the control is placed on.

The coordinates of the point where the visitor clicks is calculated and visualized by means of JavaScript (tested on IE 6.0, Firefox 1.0+, Opera 9.1). They are stored in the hidden field in order to be accessible on the server side.

function captchaClicked(hidID,e)
{
  var sender = e.target || e.srcElement;
  var offsetX, offsetY;
  
  //the calculation of the the clicked point's coordinates

  if (e.offsetX) //IE

  {
     offsetX = e.offsetX;
     offsetY = e.offsetY;
  }   
  else if (e.pageX) //Firefox, Opera

  {   
     var left = sender.offsetLeft;
     var top = sender.offsetTop;
     var parentNode = sender.offsetParent;
     while(parentNode != null && parentNode.offsetLeft != null 
                              && parentNode.offsetTop != null){
        left += parentNode.offsetLeft;
        top += parentNode.offsetTop;
        parentNode = parentNode.offsetParent;
     }
     offsetX = e.pageX - left;
     offsetY = e.pageY - top;
  }
  
  //storing of the coordinates
  document.getElementById(hidID).value = offsetX+","+offsetY;
  
  //the creation of the little red spot to mark the clicked point
  var spot = document.getElementById("spotOnCaptha");
  if (!spot)
  {
     spot = document.createElement("span");
     spot.id = "spotOnCaptha";
     spot.style.height = "3px";
     spot.style.width = "3px";
     spot.style.fontSize="1px";
     spot.style.backgroundColor="red";
     spot.style.position="absolute";
     spot.style.zIndex = 100;
     sender.parentNode.appendChild(spot);
  }
  
  //positioning of the spot

  spot.style.left = e.pageX || (event.clientX + document.body.scrollLeft);
  spot.style.top = e.pageY || (event.clientY + document.body.scrollTop);
}

Now, about the CAPTCHA image creation. In the loaded template picture, a rectangle with randomly defined coordinates is selected. Then, the coordinates are saved in the Session. It is possible to store them in the ViewState, but in this case, they have to be encoded because the ViewState is accessible on the client side. Then, the image in the rectangle boundaries is stretched (any other distortion may be used, the only condition - it has to be notable for the site visitor).

Also, here is another problem. It is possible to compare the template (original) image and the final distorted image pixel by pixel in order to find the distorted area (thanks davidbeseke for the exploit). To avoid this possibility, the template image is also changed in a random way. In the example below, it is stretched or compressed on a random scale.

//template image loading

using (System.Drawing.Image img = 
       System.Drawing.Image.FromFile(
       this.Page.MapPath(TemplateImageUrl)))
{
    //CAPCTHA image size is smaller then the template image one.

    int captchaWidth = (int)(img.Width * 0.9);
    int captchaHeight = (int)(img.Height * 0.9);

    bitmap = new Bitmap(captchaWidth, captchaHeight);
    Graphics g = Graphics.FromImage(bitmap);

    //the rectangle dimension

    int rectWidth = 20;
    int rectHeight = 20;

    Random r = new Random();

    //the original (template) image 
    //is stretched or shrunken in a random way

    float scaleX = 1 + r.Next(-100, 100) / 1000f;
    float scaleY = 1 + r.Next(-100, 100) / 1000f;
    g.ScaleTransform(scaleX, scaleY);
    g.DrawImage(img, 0, 0, img.Width, img.Height);
    g.ResetTransform();

    //the rectangle coordinates are selected in a random way too

    int x = r.Next(0, captchaWidth - rectWidth);
    int y = r.Next(0, captchaHeight - rectHeight);
    Rectangle distortedRect = new Rectangle(x, y, rectWidth, rectHeight);
    HttpContext.Current.Session["ImgCAPTCHA.coords"] = distortedRect;

    rectWidth = rectWidth + 10;
    rectHeight = rectHeight + 10;
    if (x + rectWidth > captchaWidth)
        x = captchaWidth - rectWidth;
    if (y + rectHeight > captchaHeight)
        y = captchaHeight - rectHeight;

    //draw distorted part of the image

    g.DrawImage(img, distortedRect, 
                new Rectangle(x, y, rectWidth, rectHeight), 
                GraphicsUnit.Pixel);

    g.DrawRectangle(Pens.Black, 0, 0, captchaWidth-1, captchaHeight-1);
}

Maybe the proposed image creation algorithm looks imperfect. Yes, it does. I want to say that this article is rather an idea presentation than a control's description (it is just an example).

How to use

In order to use the control, it has to assign the path of the template image to the TemplateImageUrl property. The result of the CAPTCHA control action is a value of the IsValid property. The control can be locked after a fixed number of the failed attempts to pass the test, per session (FailedAttemptsBeforeLocking and IsLocked properties). You can see how it works on the test page on my site. The full code is accessible in the demo project.

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralInteresting Idea
stixoffire
2:30 14 Dec '07  
But clicking on any coordinate - a lucky random shot or continued banging.
When using the text elements - one needs to know am I using special characters, alphabets, numbers etc.. and if I am how many per crack - say I used 8 characters.. that causes a lot of chunking to crack JUST that page, I could also capture the number of requests from that IP address, and say woops you have exceeded the limit in the time period alotted. So you might over the course of a year crack that one page - which I will expire long before then - by coding that specific page(session) and the requester as uniqueid then storing that on the server. If that page request aint found in my storage for that 1/2 hour.. (I dispose of that object every half hour)
So if I dont find it - the page has expired... Hasta la vista...do it again with a different image..
GeneralContinuation ...
Mykola Tarasyuk
8:00 31 Aug '07  
Here is the article continuation:
http://www.codeproject.com/useritems/ImageBasedCaptcha_Part2.asp
GeneralNice idea, but ...
chitty2
22:36 22 May '07  
Hi,

Nice idea, but not very accessible. What if you have someone who has visual impairments?

But nice idea in principal.

Regards,
Chitty.

http://www.leapoflogic.co.uk

GeneralRe: Nice idea, but ...
Mykola Tarasyuk
0:05 23 May '07  
Thanks
GeneralCracking this captcha
davidbeseke
4:17 22 May '07  
I'm thinking you'd need to use a pretty large image base in order to keep bots from cracking this. If I was trying to crack it, I'd download and store a bunch of your images. Depending upon how easy it was to identify the image as something I already have in my database, I would then do a pixel by pixel compare in order to find the distortion area so my bot could return the correct answer.
GeneralRe: Cracking this captcha
Mykola Tarasyuk
5:44 22 May '07  
Well, you are right. I guess you can crack current implementation in this way.
But this is only idea and the simplest implementation. It can be improved.
For example:
1.To prevent downloading of the template image it can be stored in database.
2.To prevent pixel by pixel comparison it is possible to use template image that a little more then the image transferred on the client side. Then it can be drawn with a random displacement.
Code snippet that is written offhand:
bitmap = new Bitmap(img.Width-5, img.Height-5);
Graphics g = Graphics.FromImage(bitmap);

//the rectangle dimension
int rectWidth = 20;
int rectHeight = 20;

Random r = new Random();
g.DrawImage(img, r.Next(-5, 0), r.Next(-5, 0), img.Width, img.Height);
3.To prevent pixel by pixel comparison it is possible to add some random color hue to the template image.
So I guess all is not so bad. Smile
GeneralRe: Cracking this captcha
Mykola Tarasyuk
0:06 23 May '07  
I have changed the image creation algorithm. I guess now it is complicated to crack them in the way you proposed.
GeneralI may be wrong
sk8er_boy287
22:34 20 May '07  
I don't know if this is such a good idea. The example is not very good. I didn't spot the difference until I read the text below it (the red arrow points too far to the left of the deformation, so it didn't help much).Smile
It would be really easy for an automated bot to just send all the coordinates in the image to the server (i.e, simulate a click on each pixel of the image) and figure out the returned destination url through elimination. It would probably end up with two possible destination url's, one which is the real request url, and one which points back to the image control in case the user clicked outside the deformation, for another test. By comparing url strings or by repeating the process a few times, the real url is bound to be found.
IMO, using images this way instead of the traditional text routine is actually a lot easier, since the result would be found in only IMAGE_WIDTH*IMAGE_HEIGHT attempts possibly multiplied by two (in case the url found in the first pass has to be verified on a second image). In the text-based id algorithm, all the letters of the alphabet have to be checked similar to password-hacking, which is harder and more time-consuming.
GeneralRe: I may be wrong
Mykola Tarasyuk
2:42 21 May '07  
>>I don't know if this is such a good idea. The example is not
>>very good. I didn't spot the difference until I read the text
>>below it (the red arrow points too far to the left of the deformation,
>> so it didn't help much).

Thanks for the case comment. I'll try to improve the images in the article.
Do you have the same difficulty when you work with demo project or on test page? My interest whether is it hard to notice a distortion or is it impossible to notice it?

>>It would be really easy for an automated bot to just send all the
>>coordinates in the image to the server (i.e, simulate a click on
>>each pixel of the image) and figure out the returned destination
>> url through elimination. It would probably end up with two possible
>>destination url's, one which is the real request url, and one which
>>points back to the image control in case the user clicked outside the
>> deformation, for another test.
>> By comparing url strings or by repeating the process a few times,
>>the real url is bound to be found.

Image url do not store any information about the right answer. This url is changed only to avoid caching of the image.
Coordinates are stored in Session and they are changed on each postback - also image is refreshed and the distortion will be in another place - so it is impossible to send multiple answers to the same image. Or did I misunderstand something in your message?

GeneralRe: I may be wrong
sk8er_boy287
3:18 21 May '07  
Mykola Tarasyuk wrote:
Thanks for the case comment. I'll try to improve the images in the article.
Do you have the same difficulty when you work with demo project or on test page? My interest whether is it hard to notice a distortion or is it impossible to notice it?

I haven't tested the project yet. It's just the image in the example that doesn't quite fit the example. I guess it all depends on the distortion algorithm used. You could use a Photoshop-style effect like a swirl or something that stands out better from the actual picture.

Mykola Tarasyuk wrote:
Image url do not store any information about the right answer. This url is changed only to avoid caching of the image.
Coordinates are stored in Session and they are changed on each postback - also image is refreshed and the distortion will be in another place - so it is impossible to send multiple answers to the same image. Or did I misunderstand something in your message?

I was thinking about whether it is somehow possible to send the same post to the server multiple times. I also assume the post contains some kind of id (stored in the session as cookies) for the image, besides the clicked point coordinates. In that case, it is enough to just duplicate the post message and send it a few times to figure out the answer. Example of this: a browser's "back" button returns you to the previous page (i.e, the one with the old image) for another try. Is this handled?
GeneralRe: I may be wrong
hoxha
6:16 21 May '07  
If the coordinates in the session variable are changed after an unsuccessful attempt, you can try to post the page as much as you want. The area to which your coordinates are compared has now changed.

Session state is not sent along with the response, it stays on the server and it is the user's session cookie which helps the server to identify to which session the request (or in this case post) belongs.

Reposting the same form using all available pixels will eventually give a successful match. To counter this, you can limit the amount of unsuccessful attempts per session. Limit the number of tries and clearly indicate to the user what you expect from them whenever their attempt fails.
GeneralRe: I may be wrong
Mykola Tarasyuk
12:35 21 May '07  
Thanks, it is a good idea.
GeneralRe: I may be wrong
Mykola Tarasyuk
12:34 21 May '07  
>> I guess it all depends on the distortion algorithm used. You could use
>> a Photoshop-style effect like a swirl or something that stands out
>> better from the actual picture.

Yes, the distortion algorithm is not perfect and it will probably be changed.

GeneralTest page [modified]
sk8er_boy287
23:24 21 May '07  
Oh... there[^] it was!D'Oh! Smile


-- modified at 4:33 Tuesday 22nd May, 2007

Just noticed something weird: when you don't select any point on the image and just click Test to post the answer, the result is random. I sometimes got "You are definitely a human" even if I didn't select any point on the image.Big Grin

Cheers,
Gabriel
GeneralRe: Test page
Mykola Tarasyuk
5:47 22 May '07  
Hi Gabriel,
Indeed, it is weird. I can't find how it could happen under testing. Could you define more exactly the conditions in order to reproduce this bug? Browser, maybe sequence of actions, something else?


Last Updated 20 May 2007 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010