![]() |
Web Development »
ASP.NET »
Samples
Intermediate
The image-based CAPTCHABy Mykola TarasyukThe article describes a variant of the image-based CAPTCHA. |
C#, Javascript, Windows, .NET, ASP.NET, Visual Studio, WebForms, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
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.
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 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).
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.
| You must Sign In to use this message board. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 20 May 2007 Editor: Smitha Vijayan |
Copyright 2007 by Mykola Tarasyuk Everything else Copyright © CodeProject, 1999-2009 Web19 | Advertise on the Code Project |