|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionI'm sure everyone reading this is familiar with spam. There are two schools of thought when it comes to fighting spam:
Both of these approaches have their pros and cons, of course. This article will only deal with the second technique: verifying that the data you are receiving is coming from an actual human being and not a robot or script. A CAPTCHA is a way of testing input to ensure that you're dealing with a human. Now, there are a lot of ways to build a CAPTCHA, as documented in this MSDN article on the subject, but I will be focusing on a visual data entry CAPTCHA. There's already a great ASP.NET article on a CAPTCHA control here on CodeProject, so you may be wondering what this article is for. I wanted to rebuild that solution for the following reasons:
So, this article will document how to turn a set of existing ASP.NET web pages into a simple, drag and drop ASP.NET server control -- with a number of significant enhancements along the way. ImplementationThe first thing I had to deal with was the image generated by the CAPTCHA class. This was originally done with a dedicated .aspx form-- something that won't exist for a server control. How could I generate an image on the fly? After some research, I was introduced to the world of HttpModules and HttpHandlers. They are extremely powerful -- and a single All we need is a small Web.config modification in the <httpHandlers>
<add verb="GET" path="CaptchaImage.aspx"
type="WebControlCaptcha.CaptchaImageHandler, WebControlCaptcha" />
</httpHandlers>
This handler defines a special page named CaptchaImage.aspx. Now, this "page" doesn't actually exist. When a request for CaptchaImage.aspx occurs, it will be intercepted and handled by a class that implements the Public Sub ProcessRequest(ByVal context As System.Web.HttpContext) _
Implements System.Web.IHttpHandler.ProcessRequest
Dim app As HttpApplication = context.ApplicationInstance
'-- get the unique GUID of the captcha;
' this must be passed in via querystring
Dim strGuid As String = Convert.ToString(app.Request.QueryString("guid"))
Dim ci As CaptchaImage
If strGuid = "" Then
'-- mostly for display purposes when in design mode
'-- builds a CAPTCHA image with all default settings
'-- (this won't reflect any design time changes)
ci = New CaptchaImage
Else
'-- get the CAPTCHA from the ASP.NET cache by GUID
ci = CType(app.Context.Cache(strGuid), CaptchaImage)
app.Context.Cache.Remove(strGuid)
End If
'-- write the image to the HTTP output stream as an array of bytes
ci.Image.Save(app.Context.Response.OutputStream, _
Drawing.Imaging.ImageFormat.Jpeg)
'-- let the browser know we are sending an image,
'-- and that things are 200 A-OK
app.Response.ContentType = "image/jpeg"
app.Response.StatusCode = 200
app.Response.End()
End Sub
A new CAPTCHA image will be generated, and the image streamed directly to the browser from memory. Problem solved! However, there's another problem. There has to be communication between the <img src="CaptchaImage.aspx?guid=99fecb18-ba00-4b60-9783-37225179a704"
border='0'>
This GUID (globally unique identifier) is a key used to access a CAPTCHA object that was originally stored in the ASP.NET Cache by the control. Take a look at the Private Sub GenerateNewCaptcha()
LocalGuid = Guid.NewGuid.ToString
If Not IsDesignMode Then
HttpContext.Current.Cache.Add(LocalGuid, _captcha, Nothing, _
DateTime.Now.AddSeconds(HttpContext.Current.Session.Timeout), _
TimeSpan.Zero, Caching.CacheItemPriority.NotRemovable, Nothing)
End If
Me.CaptchaText = _captcha.Text
Me.GeneratedAt = Now
End Sub
It may seem a little strange, but it works great! The sequence of ASP.NET events is as follows:
Note that there is a little cleanup involved at the end. If, for some reason, the control renders but the image URL is never retrieved, there would be an orphan CAPTCHA object in the Cache. This can happen, but should be rare in practice-- and our Cache entry only has a 20 minute lifetime anyway. One mistake I made early on was storing the actual CAPTCHA text in the ViewState. The ViewState is not encrypted and can be easily decoded! I've switched to ControlState for the GUID, which is essential for retrieving the shared Captcha control from the Cache -- but by itself, it is useless. CaptchaControl PropertiesThe
Many of these properties have to do with the inherent tradeoff between human readability and machine readability. The harder a CAPTCHA is for OCR software to read, the harder it will be for us human beings, too! For illustration, compare these two CAPTCHA images:
The CAPTCHA on the left is generated with all "medium" settings, which are a reasonable tradeoff between human readability and OCR machine readability. The CAPTCHA on the right uses a lower The ConclusionMany thanks to BrainJar for creating his simple yet effective CAPTCHA image class. Now that I've wrapped it up into an ASP.NET server control, it should be easier than ever to simply drop on a web form, set a few properties, and start defeating spammers at their own game! There are many more details and comments in the demonstration solution provided at the top of the article, so check it out. And please don't hesitate to provide feedback, good or bad! I hope you enjoyed this article. If you did, you may also like my other articles as well. History
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||