Creation of CAPTCHA Images in .NET
Experience Level - Intermediate
This is how to create a simple one your self, it may not be the most elaborate, but it will provide at least an extra layer of security to you, and will provide an ever changing result set
This has been built in SQL, with VB.NET and MVC Page Routing.
First we need to create the SQL Table to house all of our results. For high use systems, you will need to create an index on the GUID.
Create Table
CREATE TABLE [dbo].[captcha]([captchaGUID] [varchar](36) NOT NULL,[captchaValue] [varchar](10) NULL,[captchaDateAdded] [datetime] NULL,[captchaDateGuessed] [datetime] NULL)
Add a service page
Next we will create a function to generate a random letter. If you look closely at the bottom, the code will pass back empty strings for a few numbers, this way the generated values will always be of varying length.
The reason we use 40 as the value is to generate a one in 5 chance that the string (with a length of 32) returns a blank value.
Random Letter Function
CREATE FUNCTION [dbo].[GetLetter](@MyInt INT) RETURNS VARCHAR(1) AS BEGINRETURN (SELECT REPLACE(SUBSTRING('ABCDEFGHJKMNPQRTUVQXYZ0123456789', @MyInt,1),'_',''))ENDGOSELECT dbo.GetLetter(ROUND(RAND()*40,0))
Add a service page
Now we will create a stored procedure to send the record back to the web site, this is also where Captcha's that have been guessed will be generated and deleted. In this code, they will be deleted after 30 minutes.
We call the function above 10 times and concatenate the values together, and remove any older guessed values.Stored Procedure
CREATE PROC [dbo].[NewCaptchaValue] AS BEGINWHILE (SELECT COUNT(*) FROM captcha WHERE captchaDateGuessed IS NULL)<150BEGIN
INSERT INTO captcha(captchaGUID,captchaValue,captchaDateAdded)SELECT NEWID(),dbo.GetLetter(ROUND(RAND()*40,0))+dbo.GetLetter(ROUND(RAND()*40,0))+dbo.GetLetter(ROUND(RAND()*40,0))+dbo.GetLetter(ROUND(RAND()*40,0))+dbo.GetLetter(ROUND(RAND()*40,0))+dbo.GetLetter(ROUND(RAND()*40,0))+dbo.GetLetter(ROUND(RAND()*40,0))+dbo.GetLetter(ROUND(RAND()*40,0))+dbo.GetLetter(ROUND(RAND()*40,0))+dbo.GetLetter(ROUND(RAND()*40,0)),GETDATE()ENDDELETE FROM captcha WHERE captchaDateGuessed<DATEADD(MINUTE,-30,GETDATE())DECLARE @Newcaptcha VARCHAR(36)=(SELECT TOP 1 captchaGUID FROM captcha WHERE captchaDateGuessed IS NULL)SELECT @NewcaptchaEND
Add a service page
I decided to send back a string of Pass/Fail.
Stored Procedure
CREATE PROC [dbo].[CaptchaValue](@captchaGUID VARCHAR(36),@captchaValue VARCHAR(10)) AS BEGINDECLARE @CaptchaValueDB VARCHAR(10)=(SELECT captchaValue FROM captcha WHERE captchaGUID=@captchaGUID)DECLARE @Newcaptcha VARCHAR(36)UPDATE captcha SET captchaDateGuessed=GETDATE() WHERE captchaGUID=@captchaGUIDIF @captchaValue=@CaptchaValueDBBEGINSET @Newcaptcha='Pass'ENDIF @captchaValue<>@CaptchaValueDB OR @CaptchaValueDB IS NULLBEGINSET @Newcaptcha='Fail'ENDSELECT @NewcaptchaEND
Switch to the Academy Project in Visual Studio
A couple of steps to follow here
- If the project doesn't already have it, add the App_Code folder as per screenshot
- Right Mouse button on the App_Code folder and select Add>Class
- A pop-up should appear, call this class Captcha.vb
Right click on this new item and select properties.
Above the Public Class declaration add the following reference;
Imports System.Drawing
Switch to the Academy Project in Visual Studio
Private Shared Function GenerateLineNumberY() As Integer Dim ret As New Integer ret = (6 * Right((Rnd() * Rnd() * Rnd()), 1)) + 2 Return ret End Function Private Shared Function GenerateLineNumberX() As Integer Dim ret As New Integer ret = (6 * Right((Rnd() * Rnd() * Rnd()), 1)) + 2 Return ret End Function
Switch to the Academy Project in Visual Studio
Private Shared Function GenerateLineNumberSpace() As Integer Dim ret As New Integer ret = (Right(Rnd(), 1)) + 13 Return ret End Function Private Shared Function GenerateLineNumberHeight() As Integer Dim ret As New Integer ret = (2 * Right((Rnd() * Rnd() * Rnd()), 1)) + 1 Return ret End Function
Switch to the Academy Project in Visual Studio
Private Shared Function GenerateRandomPenColor() As System.Drawing.Pen Dim mypen As New Pen(Brushes.Cyan) Dim ret As New Integer ret = Right((Rnd() * Rnd() * Rnd()), 1) Select Case ret Case 5, 0, 1 mypen.Color = Color.DarkGray Case 6, 2 mypen.Color = Color.DarkRed Case 7, 3, 9 mypen.Color = Color.DarkOliveGreen Case 8, 4 mypen.Color = Color.DarkOrange End Select Return mypen End Function Private Shared Function GenerateRandomBrushColor() As System.Drawing.SolidBrush Dim myBrush As New SolidBrush(Color.Aqua) Dim ret As New Integer ret = Right((Rnd() * Rnd() * Rnd()), 1) Select Case ret Case 5, 0, 1 myBrush.Color = Color.DarkGray Case 6, 2 myBrush.Color = Color.DarkRed Case 7, 3, 9 myBrush.Color = Color.DarkOliveGreen Case 8, 4 myBrush.Color = Color.DarkOrange End Select Return myBrush End Function Public Shared Function GenerateCaptcha(CaptchaString As String) As Bitmap Dim Height As Integer = 60 Dim Width As Integer = 300 Dim objBitmap As Bitmap Dim objGraphics As Graphics
Dim oPoint As New PointF objBitmap = New Bitmap(Width, Height) objGraphics = Graphics.FromImage(objBitmap) 'Draw Background objGraphics.FillRectangle(Brushes.White, 0, 0, Width, Height) objGraphics.DrawRectangle(Pens.Black, 0, 0, Width - 2, Height - 2) 'Draw Text Dim oFont As New Font("Arial", 20) Dim spac As Integer = 5 Dim stringInt As Integer = 1 While stringInt <= CaptchaString.Length spac = spac + GenerateLineNumberSpace() oPoint = New PointF(spac, GenerateLineNumberHeight) objGraphics.DrawString(Mid(CaptchaString, stringInt, 1), oFont, GenerateRandomBrushColor, oPoint) stringInt = stringInt + 1 End While 'White Lines objGraphics.DrawLine(Pens.White, GenerateLineNumberX, GenerateLineNumberY, Width - GenerateLineNumberX(), Height - GenerateLineNumberY()) objGraphics.DrawLine(Pens.White, GenerateLineNumberX, GenerateLineNumberY, Width - GenerateLineNumberX(), Height - GenerateLineNumberY()) objGraphics.DrawLine(Pens.White, GenerateLineNumberX, GenerateLineNumberY, Width - GenerateLineNumberX(), Height - GenerateLineNumberY()) objGraphics.DrawLine(Pens.White, GenerateLineNumberX, GenerateLineNumberY, Width - GenerateLineNumberX(), Height - GenerateLineNumberY()) objGraphics.DrawLine(Pens.White, GenerateLineNumberX, GenerateLineNumberY, Width - GenerateLineNumberX(), Height - GenerateLineNumberY()) objGraphics.DrawLine(Pens.White, GenerateLineNumberX, GenerateLineNumberY, Width - GenerateLineNumberX(), Height - GenerateLineNumberY()) objGraphics.DrawLine(Pens.White, GenerateLineNumberX, GenerateLineNumberY, Width - GenerateLineNumberX(), Height - GenerateLineNumberY()) objGraphics.DrawLine(Pens.White, GenerateLineNumberX, GenerateLineNumberY, Width - GenerateLineNumberX(), Height - GenerateLineNumberY()) objGraphics.DrawLine(Pens.White, GenerateLineNumberX, GenerateLineNumberY, Width - GenerateLineNumberX(), Height - GenerateLineNumberY()) 'Draw Lines objGraphics.DrawLine(GenerateRandomPenColor, GenerateLineNumberX, GenerateLineNumberY, Width - GenerateLineNumberX(), Height - GenerateLineNumberY()) objGraphics.DrawLine(GenerateRandomPenColor, GenerateLineNumberX, GenerateLineNumberY, Width - GenerateLineNumberX(), Height - GenerateLineNumberY())
objGraphics.DrawRectangle(Pens.Black, 0, 0, Width - 2, Height - 2)
Return objBitmap End Function
Find the sub RegisterRoutes and add a new route (Routes.MapPageRoute) inside.
Sub RegisterRoutes(ByVal Routes As RouteCollection) Routes.MapPageRoute("Captcha", "Captcha/{GUID}", "~/Data/Images/CaptchaService.aspx") End Sub
Add a service page
To add the data folder, use the following options;
- Right click on ClaytabaseAcademy and use the Add>New Folder option, name the folder Data.
- Now add another folder called Images to the Data Folder
- Next, add a Webform called CaptchaService.aspx to the Images folder
This page will get called by the route handler we created earlier.
Replace Code
Imports System.Data.SqlClientImports ClaytabaseAcademy.GlobalVariablesImports ClaytabaseAcademy.CaptchaImports System.DrawingImports System.Drawing.ImagingPublic Class CaptchaService Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim captchaguid As String = RouteData.Values("GUID").ToString Dim con As New SqlConnection(ConStr) Dim com As New SqlCommand("SELECT captchaValue FROM captcha WHERE captchaGUID='" + captchaguid + "'", con) con.Open() Dim CaptchaBitmap As Bitmap = GenerateCaptcha(com.ExecuteScalar) con.Close() Response.ClearContent() Response.ContentType = "image/bmp" CaptchaBitmap.Save(Response.OutputStream, ImageFormat.Gif) End SubEnd Class
Add a service page
Create a new folder called Pages to the root of the project by Right Clicking Claytabase Academy and Add > New Folder
Add a new web form called CaptchaView
Create a new web page, and drag in an image control, textbox, button, hiddenfield and a label or copy the code below into the body (HTML Part) of the page.<body> <form id="form1" runat="server" style="margin:100px auto;width:300px;padding:10px;background-color:#fff;box-shadow:0 0 3px 0 #222;border-radius: 8px;"> <div style="text-align:center;"> <asp:Image ID="CaptchaImage" runat="server" /> </div> <div style="text-align:center;"> <asp:TextBox ID="captchaEntered" runat="server"></asp:TextBox> </div> <div style="text-align:center;"> <asp:Button ID="captchaSubmit" runat="server" Text="Submit" style="margin:5px auto"/> <asp:HiddenField ID="captchaGuid" runat="server" /> </div> <div style="text-align:center;"> <asp:Label ID="captchaResult" runat="server" Text=""></asp:Label> </div> </form></body>
Last step is to test that the code works. For this we need to go into the code behind (right click>view code) and update the code behind to what we have below.
This creates a sub that runs on page load, and another that handles the click of the button we added.
Imports System.Data.SqlClientImports ClaytabaseAcademy.GlobalVariablesPublic Class CaptchaView Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load If Not IsPostBack Then Using con As New SqlConnection(ConStr) Using com As New SqlCommand("EXEC dbo.NewCaptchaValue", con) con.Open() Dim Captcha As String = com.ExecuteScalar captchaGuid.Value = Captcha CaptchaImage.ImageUrl = "/Captcha/" + Captcha End Using End Using End If End Sub Protected Sub GetCaptcha(sender As Object, e As EventArgs) Handles captchaSubmit.Click If IsPostBack Then Using con As New SqlConnection(ConStr) Dim valStr As String = Replace(captchaEntered.Text, "'", "''") Dim valGUID As String = Replace(captchaGuid.Value, "'", "''") Response.Write("valStr: " + valStr + "<br>") Response.Write("valGUID: " + valGUID + "<br>") Using com As New SqlCommand("EXEC CaptchaValue '" & valGUID & "','" & valStr & "'", con) con.Open() Dim res As String = com.ExecuteScalar() captchaResult.Text = res End Using Response.Write("EXEC CaptchaValue '" & valGUID & "','" & valStr & "'") End Using End If End SubEnd Class
Test that it has worked by select Build > Build Solution from the Top Menu
You should now be able to test this in a number of ways;- Click on Debug when on the relevant page
- Right Mouse button on the item within the project explorer and selecting View in Browser
- Right Mouse button on the page and select View in Browser
Website design by Claytabase
This is a section of code that has been modified from Ousia Content Management System code, one of the quickest and most optimised systems on the market, part of our website design services.
These are available with sites starting at around £500.