Click here to Skip to main content
Click here to Skip to main content
Go to top

Generating Captcha images – ASP.NET MVC

, 4 Dec 2011
Rate this:
Please Sign up or sign in to vote.
In this article, we will define our implementation to handle Captcha images.

Wikipedia describes Captcha images as:

a type of challenge-response test used in computing as an attempt to ensure that the response is generated by a person

There are a few simple plug-ins that you can use in your ASP.NET MVC app, like reCaptcha, but to have a fully flexible and integrated solution, you have to create your own. In this article, we will define our implementation to handle Captcha images.

Start by creating an empty MVC 3 Internet application.

Go to the HomeController.vb class and add the following Action method.

HomeController.vb

GetCaptcha Action

Function GetCaptcha() As ActionResult
    'Captcha Image Size  Width - Height
    Dim width As Integer = 75
    Dim height As Integer = 20

    'Captcha String
    Dim fontFamily = "Tahoma"
    ' -  Generate Random
    Dim randomsize As Integer = 5
    Dim randomstringbuilder As New StringBuilder()
    Dim random As New Random(DateTime.Now.Millisecond)
    Dim ch As Char
    For i As Integer = 0 To randomsize - 1
        ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)))
        randomstringbuilder.Append(ch)
    Next
    Dim captchaString = randomstringbuilder.ToString

    ' Create a new 32-bit bitmap image.
    Dim bitmap As New Drawing.Bitmap(width, height, _
        Drawing.Imaging.PixelFormat.Format32bppArgb)

    ' Create a graphics object for drawing.
    Dim g As Drawing.Graphics = Drawing.Graphics.FromImage(bitmap)
    g.SmoothingMode = Drawing.Drawing2D.SmoothingMode.AntiAlias
    Dim rect As New Drawing.Rectangle(0, 0, width, height)

    ' Fill in the background.
    Dim hatchBrush As New Drawing.Drawing2D.HatchBrush(_
      Drawing.Drawing2D.HatchStyle.Wave, _
      Drawing.Color.LightGray, Drawing.Color.White)
    g.FillRectangle(hatchBrush, rect)

    ' Set up the text font.
    Dim size As Drawing.SizeF
    Dim fontSize As Single = rect.Height + 1
    Dim font As Drawing.Font
    Dim format As New Drawing.StringFormat()
    format.Alignment = Drawing.StringAlignment.Center
    format.LineAlignment = Drawing.StringAlignment.Center

    ' Adjust the font size until the text fits within the image.
    Do
        fontSize -= 1
        font = New Drawing.Font(fontFamily, fontSize, Drawing.FontStyle.Bold)
        size = g.MeasureString(captchaString, font, _
               New Drawing.SizeF(width, height), format)
    Loop While size.Width > rect.Width

    ' Create a path using the text and warp it randomly.
    Dim path As New Drawing.Drawing2D.GraphicsPath()
    path.AddString(captchaString, font.FontFamily, _
         CInt(font.Style), font.Size, rect, format)
    Dim v As Single = 4.0F
    Dim points As Drawing.PointF() = {New Drawing.PointF(random.[Next](rect.Width) / v, _
       random.[Next](rect.Height) / v), New Drawing.PointF(rect.Width - _
       random.[Next](rect.Width) / v, random.[Next](rect.Height) / v), _
       New Drawing.PointF(random.[Next](rect.Width) / v, _
       rect.Height - random.[Next](rect.Height) / v), _
       New Drawing.PointF(rect.Width - random.[Next](rect.Width) / v, _
       rect.Height - random.[Next](rect.Height) / v)}
    Dim matrix As New Drawing.Drawing2D.Matrix()
    matrix.Translate(0.0F, 0.0F)
    path.Warp(points, rect, matrix, Drawing.Drawing2D.WarpMode.Perspective, 0.0F)

    ' Draw the text.
    hatchBrush = New Drawing.Drawing2D.HatchBrush(_
      Drawing.Drawing2D.HatchStyle.DashedUpwardDiagonal, _
      Drawing.Color.DarkGray, Drawing.Color.Black)
    g.FillPath(hatchBrush, path)

    ' Add some random noise.
    Dim m As Integer = Math.Max(rect.Width, rect.Height)
    For i As Integer = 0 To CInt(Math.Truncate(rect.Width * rect.Height / 30.0F)) - 1
        Dim x As Integer = random.[Next](rect.Width)
        Dim y As Integer = random.[Next](rect.Height)
        Dim w As Integer = random.[Next](m \ 50)
        Dim h As Integer = random.[Next](m \ 50)
        g.FillEllipse(hatchBrush, x, y, w, h)
    Next

    ' Clean up.
    font.Dispose()
    hatchBrush.Dispose()
    g.Dispose()

    Dim captchaImageResult As FileContentResult = Nothing
    Using mystream As New System.IO.MemoryStream()

        bitmap.Save(mystream, System.Drawing.Imaging.ImageFormat.Jpeg)
        captchaImageResult = MyBase.File(mystream.GetBuffer, "image/jpeg")

    End Using
    bitmap.Dispose()

    'Save the Captcha Hash in the Session
    Session("botdeflector") = _
      Convert.ToBase64String(System.Security.Cryptography.MD5.Create().
      ComputeHash(Encoding.UTF8.GetBytes(captchaString)))

    'Return the Captcha Image
    Return captchaImageResult

End Function

What is mostly happening here is the generation of the image, pasting in the text that you want for the Captcha image, adding some noise, and in the end, we save the hash for the generated text. This is what we will use later to verify that the user submitted the correct string of text. The most important part is saving the hash to the session state.

Change the index page a little, adding a new form, an image tag with its source set as the captcha image, and a submit button.

Index.vbhtml

@Code
    ViewData("Title") = "Home Page"
End Code
<h2>@ViewData("Message")</h2>

@Using Html.BeginForm

    @<div>
        <img src='@Url.Action("GetCaptcha")' alt='Captcha' />
        <input type="text" size="10" 
           name="CaptchaAnswer" autocomplete="off" />
        <br />
        <input type="submit" value="Submit"/>
    </div>

End Using

Add a new POST only index Action method.

HomeController.vb

Index Action (POST)

<HttpPost()>
Function index(CaptchaAnswer As String) As ActionResult

    Dim captchaHash = Session("botdeflector")

    If captchaHash = Convert.ToBase64String(_
       System.Security.Cryptography.MD5.Create().ComputeHash(_
       Encoding.UTF8.GetBytes(UCase(CaptchaAnswer)))) Then

        'Valid
        ViewData("Message") = "YOU ARE A PERSON :>"

    Else
        'Invalid
        ViewData("Message") = "YOU ARE A ROBOT!"

    End If

    Return View()

End Function

The validation begins with converting the Captcha answer the user supplied to its hash equivalent, then we get our saved hash from the current user session, compare them, and return the result.

Here is the complete project if you want it. And you can also use the following helper functions for random generation of strings or numbers:

Generate Random Number Function

Private Function generateRandomNumber(ByVal size As Integer) As String

    Dim builder As New StringBuilder()
    Dim random As New Random(DateTime.Now.Millisecond)
    Dim ch As Char
    For i As Integer = 0 To size - 1
        ch = random.Next(9).ToString
        builder.Append(ch)
    Next
    Return builder.ToString()

End Function

Generate Random String Function

Private Function generateRandomString(ByVal size As Integer) As String

    Dim builder As New StringBuilder()
    Dim random As New Random(DateTime.Now.Millisecond)
    Dim ch As Char
    For i As Integer = 0 To size - 1
        ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)))
        builder.Append(ch)
    Next
    Return builder.ToString()

End Function

Screenshot of Demo Site

Screenshot of Demo Site - Captcha Image Generator

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Buaziz
Software Developer (Senior) KUFPEC
Kuwait Kuwait
No Biography provided
Follow on   Twitter   Google+

Comments and Discussions

 
SuggestionSuggestion... PinmemberSledgeHammer0130-Nov-11 9:55 
GeneralRe: Suggestion... PinmemberBuaziz4-Dec-11 18:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 5 Dec 2011
Article Copyright 2011 by Buaziz
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid