Prizes & Awards
My Profile
Active Members
TodayLast 7 Days
more...
|
Resources » Articles » .NET Framework »
CAPTCHA in VB.NET
|
This article will guide you through creating a CAPTCHA security image for your web application in VB.Net using Visual Studio 2005. Before we jump into the code lets take a look at what a CAPTCHA is.
CAPTCHA stands for "Completely Automated Public Turing Test To Tell Computers And Humans Apart." What does this mean? It is a program that can tell humans from machines using some type of generated test. A test most people can easily pass but a computer program cannot. The term CAPTCHA was coined in 2000 by Luis von Ahn, Manuel Blum, Nicholas Hopper and John Langford of Carnegie Mellon University. These 4 individuals are credited with creating the first CAPTCHA, which was used by Yahoo!
CAPTCHA’s have many practical uses for security, including, but not limited to:
- To prevent “comment spam” in user blogs.
- Protecting registration at websites (prevents people from having “bots” to register many accounts for the purpose of spam).
- Protecting email addresses from “Scrapers” (A “Scraper” is a software bot that searches the Internet for email addresses in plain text).
- Help prevent Dictionary Attacks.
When I first set out to create my own CAPTCHA for a website I was working on for a client, I searched and searched the Internet for a “ready made” one. I found a few simple examples, most done in C# (not that that really matters as I can read/program in both) but I wanted a VB.Net version, which was nowhere to be found. I wanted to offer anyone who used my CAPTCHA more flexibility and control than the simpler versions I had found offered, such as the ability to chose their primary color, secondary color and even the HatchStyle for the HatchBrush. So without further adieu let’s see some code (This is kind of a long class, over 420 lines, so bear with me).
The first thing you must do with the class is to ensure you have the proper references:
Imports System Imports System.Drawing Imports System.Drawing.Imaging Imports System.Drawing.Drawing2D Imports System.Drawing.Text Imports System.Windows.Forms Imports System.Reflection
For some of these (depends on how you have your IDE setup) you may have to go Project > Add Reference and add a reference to the Namespace’s listed above. After than you need your Global Variables (both property variables and regular global)
‘Property variables Private _text As String Private _width As Integer Private _height As Integer Private _failure As String Private _hatch As HatchStyles Private _mainColor As Color Private _secondaryColor As Color
‘Global variables Private _image As Bitmap Private ran As Random = New Random Private fontFamily As String
Next we need to declare our object properties ( we have Read-only and Read/Write properties). The Read/Write properties (only 1 at this time) is for holding the failure message to the end user if they didn’t type the correct CAPTCHA. This is one of the features I wanted in my version of the CAPTCHA.
The ReadOnly properties are much larger in number, these properties are for: 1. Random string to be displayed 2. The image itself 3. The width of the image 4. The height of the image 5. The main color * 6. The secondary color * 7. The HatchStyle of the HatchBrush *
• These are 3 features I wanted to give the developer control over, a way to customize it for their needs and not be stuck with a version where someone “told” then what to use.
‘Read/Write properties ''' ''' Property to hold the failure message to be displayed to the user ''' ''' A string the developer choses to use ''' The string message (or a NullReferenceException if it isnt provided) ''' Public Property FailureMessage() Get Return _failure End Get Set(ByVal value) If String.IsNullOrEmpty(value) Then Throw New NullReferenceException("A return message wasn't specified and is required.") _failure = String.Empty Else _failure = value End If End Set End Property
‘ReadOnly Properties ''' ''' Readonly property to hold the text of the CAPTCHA image ''' ''' ''' String value ''' Public ReadOnly Property Text() As String Get Return _text End Get End Property
''' ''' Readonly property to hold the image ''' ''' ''' A bitmap image ''' Public ReadOnly Property Image() As Bitmap Get Return _image End Get End Property
''' ''' Readonly property to hold the width of the image ''' ''' ''' Int value ''' Public ReadOnly Property Width() As Integer Get Return _width End Get End Property
''' ''' Readonly property to hold the height of the image ''' ''' ''' Int value ''' Public ReadOnly Property Height() As Integer Get Return _height End Get End Property
''' ''' Readonly property to hold the main color of the image ''' ''' ''' ''' Public ReadOnly Property MainColor() As Color Get Return _mainColor End Get End Property
''' ''' Readonly property to hold the secondaty color of the image ''' ''' ''' ''' Public ReadOnly Property SecondaryColor() As Color Get Return _secondaryColor End Get End Property
''' ''' Readonly property to hold the style of HatchBrush used ''' ''' ''' ''' Public ReadOnly Property HatchType() As HatchStyles Get Return _hatch End Get End Property
The next step in the class are 2 Enumerations. The first Enum holds the default height and width of the CAPTCHA. I chose to do this with an Enum so if the developer wanted to change the default size he only had to change the dimensions in one place, not multiple. The second Enum holds several HatchStyle’s for the HatchBrush. I only provide 8 at this time but I will be adding more in the future, this way the developer/user of the component isn’t tied into what I said the style should be. Lets take a look at them
‘Enumeration to hold the default dimensions of the CAPTCHA Enum DefaultPicSize Width = 150 Height = 60 End Enum
‘Enumerationh to hold HatchStyle values of the HatchBrush Enum HatchStyles LargeConfetti = HatchStyle.LargeConfetti Grid = HatchStyle.LargeGrid Cross = HatchStyle.DiagonalCross LargeCheckerBoard = HatchStyle.LargeCheckerBoard ZigZag = HatchStyle.ZigZag Diamond = HatchStyle.SolidDiamond SmallCheckerBoard = HatchStyle.SmallCheckerBoard SmallConfetti = HatchStyle.SmallConfetti End Enum
Next we need a way to instantiate the object, and finalize & dispose of it when we’re done with it (cleaning up is very important). There are 4 “New()” constructors with this component, which gives you a variety of ways to create a new one. You can create one with just the text to display on the image (all other values will be the default values) all the way to specifying the text, size, font, primary & secondary colors and hatch style. For finalizing we have a Overrides Finalize method, and we have 2 Dispose methods (one is a Overrides utilizing a Boolean “disposing” variable).
Public Sub New(ByVal s As String) _text = s _hatch = HatchStyles.LargeConfetti _mainColor = Color.Gray _secondaryColor = Color.White SetPicSize(DefaultPicSize.Height, DefaultPicSize.Height) SetFont() BuildCAPTCHA() End Sub
Public Sub New(ByVal s As String, ByVal width As Integer, ByVal height As Integer) _text = s _hatch = HatchStyles.LargeConfetti _mainColor = Color.Gray _secondaryColor = Color.White SetPicSize(width, height) SetFont() BuildCAPTCHA() End Sub
Public Sub New(ByVal s As String, ByVal width As Integer, ByVal height As Integer, ByVal fontName As String) _text = s _hatch = HatchStyles.LargeConfetti _mainColor = Color.Gray _secondaryColor = Color.White SetPicSize(width, height) SetFont(fontName) BuildCAPTCHA() End Sub
Public Sub New(ByVal s As String, ByVal width As Integer, ByVal height As Integer, ByVal fontName As String, _ ByVal hatch As HatchStyles, ByVal maincolor As Color, ByVal secondarycolor As Color) _text = s _hatch = hatch _mainColor = maincolor _secondaryColor = secondarycolor SetPicSize(width, height) SetFont(fontName) BuildCAPTCHA() End Sub
''' ''' Finalize method ''' ''' Protected Overrides Sub Finalize() Dispose(False) End Sub
''' ''' Dispose method ''' ''' Public Sub Dispose() GC.SuppressFinalize(Me) Dispose(True) End Sub
Protected Overridable Sub Dispose(ByVal disposing As Boolean) If disposing Then Image.Dispose() End If End Sub
In the New() constructors you see 2 methods being called, SetPicSize and SetFont. These 2 methods allow the user of this component to specify both the dimensions of the CAPTCHA and the font to use. With the font, if it’s a font that isn’t installed on the end user’s computer it will default to a System Font (to avoid it bombing on the end user if they don’t have the font the developer specified), and I also suggest you specify a font that is known to be on most, if not all, computers. Lets have a look at those methods.
Private Sub SetPicSize(ByVal width As Integer, ByVal height As Integer) 'Check to make sure they didnt supply a width of zero, if they did then throw an exception If width <= 0 Then Throw New ArgumentOutOfRangeException("width", width, "Argument out of range, must be greater than zero.") 'Check to make sure they didnt supply a height of zero, if they did then throw an exception If height <= 0 Then Throw New ArgumentOutOfRangeException("height", height, "Argument out of range, must be greater than zero.")
'Set the width & height of the image _width = width _height = height End Sub
Private Sub SetFont(Optional ByVal fontName As String = "") Dim font As Font Try ‘If the user specified a font then use it If Not String.IsNullOrEmpty(fontName) Then font = New Font(fontName, 12.0F) fontName = fontName Else ‘Otherwise default to Verdana font = New Font("Verdana", 12.0F) fontName = fontName End If font.Dispose() Catch ex As Exception ‘This ensures that if a font is specified that isnt on the end users ‘computer than a system font will be used fontName = System.Drawing.FontFamily.GenericSerif.Name End Try End Sub
Next we need a random string to display on the CAPTCHA. If you look at the CAPTCHA’s being used on the Internet today there isn’t a set size of this string, so I decided to give the option of specifying how long you want your string to be. This gives the developer a little bit of flexibility when using this component. To generate the random string I used:
Public Function GenerateRandomString(ByVal size As Integer) As String Dim captchaString As String = "" Dim iCount As Integer = 0 Try ‘Start your loop While iCount < size ‘Grab a random character and append it to the string captchaString = String.Concat(captchaString, ran.Next(10).ToString) System.Math.Min(System.Threading.Interlocked.Increment(iCount), iCount - 1) End While Catch ex As Exception MessageBox.Show(ex.Message, "Error Generating Code", MessageBoxButtons.OK, MessageBoxIcon.Error) captchaString = String.Empty End Try Return captchaString End Function
The only thing we have left to do is generate the CAPTCHA image. This is the large function in all of this, as it has to do a lot of work. To generate the image we start with a 32-bit bitmap, then we create a graphics object. From there we fill in the background of the graphics object (using the hatch style and 2 colors the developer specifies). From there we get the font ready, we then adjust the font (done in a loop) until it fits in the image properly. Then we warp the text randomly, add some random noise (making it even harder for a bot to read it), then finally we clean everything up. Since this image is created at run-time, there’s no image saved anywhere, no way for a “bot” to retrieve the image and try to process it. Here is the final function in this component.
Private Sub BuildCAPTCHA() 'Create a new 32-bit bitmap image. Dim captchaImage As Bitmap = New Bitmap(Width, Height, PixelFormat.Format32bppArgb)
'Create a graphics object for drawing. Dim oGraphic As Graphics = Graphics.FromImage(captchaImage) oGraphic.SmoothingMode = SmoothingMode.AntiAlias Dim rect As Rectangle = New Rectangle(0, 0, Width, Height)
'Fill in the background. Dim hbBrush As HatchBrush = New HatchBrush(_hatch, MainColor, SecondaryColor) oGraphic.FillRectangle(hbBrush, rect)
'Set up the text font. Dim size As SizeF Dim captchaFontSize As Integer = rect.Height + 15 Dim captchaFont As Font
'Adjust the font size until the text fits within the image. Do System.Math.Max(Threading.Interlocked.Decrement(captchaFontSize), captchaFontSize + 1) captchaFont = New Font(fontFamily, captchaFontSize, FontStyle.Bold) size = oGraphic.MeasureString(Text, captchaFont) Loop While size.Width > rect.Width
'Set up the text format. Dim format As New StringFormat format.Alignment = StringAlignment.Center format.LineAlignment = StringAlignment.Center
'Create a path using the text and warp it randomly. Dim gpCaptchaPath As GraphicsPath = New GraphicsPath gpCaptchaPath.AddString(Text, captchaFont.FontFamily, CType(captchaFont.Style, Integer), captchaFont.Size, rect, format) Dim captchaSingle As Single = 4.0F
Dim points As PointF() = {New PointF(ran.Next(rect.Width) / captchaSingle, ran.Next(rect.Height) / captchaSingle), _ New PointF(rect.Width - ran.Next(rect.Width) / captchaSingle, ran.Next(rect.Height) / captchaSingle), _ New PointF(ran.Next(rect.Width) / captchaSingle, rect.Height - ran.Next(rect.Height) / captchaSingle), _ New PointF(rect.Width - ran.Next(rect.Width) / captchaSingle, rect.Height - ran.Next(rect.Height) / captchaSingle)}
Dim captchaMatrix As Matrix = New Matrix captchaMatrix.Translate(0.0F, 0.0F) gpCaptchaPath.Warp(points, rect, captchaMatrix, WarpMode.Perspective, 0.0F)
'Draw the text. hbBrush = New HatchBrush(HatchStyle.LargeConfetti, Color.LightGray, Color.DarkGray) oGraphic.FillPath(hbBrush, gpCaptchaPath)
'Add some random noise. Dim m As Integer = Math.Max(rect.Width, rect.Height) Dim iCount As Integer = 0 While iCount < CType((rect.Width * rect.Height / 30.0F), Integer) Dim xAxis As Integer = ran.Next(rect.Width) Dim yAxis As Integer = ran.Next(rect.Height) Dim width As Integer = ran.Next(m / 50) Dim height As Integer = ran.Next(m / 50) oGraphic.FillEllipse(hbBrush, xAxis, yAxis, width, height) System.Math.Min(System.Threading.Interlocked.Increment(iCount), iCount - 1) End While
'Clean up after ourselves captchaFont.Dispose() hbBrush.Dispose() oGraphic.Dispose()
'Set the final image and return it _image = captchaImage End Sub
That is how to create a CAPTCHA in VB.Net. At first working with the System.Drawing Namespace was very scary for me, but after creating this CAPTCHA it’s not as bad as it seems. This tutorial should also give you a very good dive into working with the System.Drawing Namespace, what it can do, how to do certain things. If, after playing with this code, you are having a hard time understanding how to make it work on your site feel free to either send me a private message, or better yet post a comment here and I will be more than happy to post an example of it in action, to help you along
I have no problems with anyone using this commercially, but if you do it would be nice if there was a mention of me somewhere, if not, that’s okay too. I just hope this tutorial helps at least one person, if I can help one person at a time then I am giving back to the community that has helped me get to where I am today.
I hope you enjoyed this tutorial and thanks for reading.
For more details, visit http://www.captcha.net/
|
Responses
|
No responses found. Be the first to respond and make money from revenue sharing program.
|
|