Introduction
CAPTCHA is actually an acronym that stands for Completely Automated Public Turing test to tell bots and Humans Apart. It is easy for humans to solve, but hard for “bots” and other malicious software to figure out.
We usually use ReCaptcha in order to protect our web application from bots and spam. In general, most of the open websites uses Google ReCpatcha service to protect their website from bots interaction.
However, the Problem is Security since Google recaptcha works on pure JavaScript API and it will inject that JavaScript code at run time into your respective page of the website to generate the image for recaptcha. It can potentially be harmful to allow external JavaScript into your web application at runtime.
In addition, to use Google FREE rechaptcha API, it's required that we need to accommodate the external JavaScript into our environment and web application, which is a risk into your secure enterprise application where using external JavaScript is not permitted.
Therefore, here is the full solution to build your own for ReCaptcha without any external third party plugins Like Google reCAPTCHA, etc.
It’s a simple code developed with GenricHandler, MVC 4.0, Bootstrap, Jquery AJAX, and CSS.
In the example, I have two solutions - one for basic use and the other for complex use.
- Simple ReCaptcha
- Complex ReCaptch
Note: It has required Bootstrap Package to design Get Bootstrap from here.
Output demo as shown below.
Demo
Background
In the current internet world, tools like ReCaptcha are necessary for every web application. They protects web applications from malicious attacks or bots interaction to web form of the web application. So most of the web applications use free recapcha services provided by Google, etc. which will deal with the run time JavaScript.
However, some applications create their own recaptcha tool to support and protect their web application from unwanted activity by bots.
This solution also solves this problem with the help of using generic handler and C# code and is able to create a fully functional recaptcha code to use.
Using the Code
The code starts from the basic web pages, which includes four main pages, one extra bit for complex recaptcha code.
- _Layout.cshtml
- Index.cshtml
- HomeContoller.cs
- GenerateRecaptcha.ashx (Simple Recaptcha Code)
- GenerateRecaptcha2.ashx (Complex Recaptcha Code)
Step 1
To create this small recaptcha tool, first folder structure is very important as I am using MVC here.
Create the folder structure shown below for your web application.
Folder Structure
Step 2
Creating _Layout.cshtml file (default master file created by MVC template - just edit as per your need).
_Layout.cshtml file to provide the main page layout and incorporated all required jQuery or CSS like bootstrap.css.
This is an option page for reference to your respective master page; it will help you to integrate the recaptcha tool into your web application.
Code of _Layout.cshtml will look like below.
_Layout.cshtml
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<meta name="viewport" content="width=device-width" />
<link href="~/Content/css/bootstrap.css" rel="stylesheet" />
<script src="~/Content/js/jquery-1.11.1.js"></script>
<script src="~/Content/js/Common.js"></script>
</head>
<body class="container">
<div class="">
<h1>Recaptcha Demo <small>.Net Recaptcha with MVC and Responsive
Design with bootstrap</small></h1>
</div>
<div>
<div id="body">
@RenderSection("featured", required: false)
<section class="container-fluid">
@RenderBody()
</section>
</div>
</div>
<footer>
<div class="container-fluid">
<div class="left">
<p>MVC </p>
</div>
</div>
</footer>
@RenderSection("scripts", required: false)
</body>
</html>
Step 3
We need one more page where we can use the recaptcha tool, so I am using the default MVC index page to accommodate my recaptcha request and provide the container to hold it.
Code of Index.cshtml will look like below.
Index.cshtml
@model ReCaptchaDemo.Models.Employee
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<style type="text/css">
.Recaptcha
{
background-color: #428bca;
border: 5px solid #428bca;
border-radius: 5px;
width: 295px;
}
.RecaptchaEnterCode
{
margin-top: 15px;
background-color: #f5f5f5;
border: 1px solid #e3e3e3;
border-radius: 8px;
padding: 8px;
}
.Recaptcha2
{
background-color: darkred;
border: 5px solid darkred;
border-radius: 5px;
width: 245px;
}
.RecaptchaEnterCode2
{
margin-top: 15px;
background-color: cornsilk;
border: 1px solid cornsilk;
border-radius: 8px;
padding: 8px;
}
</style>`
'<div class="panel panel-primary">`
<div class="panel-heading">
<h3 class="panel-title">Form With Recaptcha</h3>
</div>
<div class="panel-body row">
@using (Html.BeginForm("ValidateRecaptcha2", "Home",
FormMethod.Post, new { id = "mvcForm2" }))
{
<div class="col-xs-6">
<h3>Simple reCAPTCHA</h3>
<div class="form-horizontal well" role="form">
<div class="form-group">
<label class="col-xs-4 control-label">Fisrt Name</label>
<div class="col-xs-8">
@Html.TextBoxFor(m => m.FirstName,
new { @class = "form-control",
id = "fName", placeholder = "First Name",
type = "text" })
</div>
</div>
<div class="form-group">
<label class="col-xs-4 control-label">Last Name</label>
<div class="col-xs-8">
@Html.TextBoxFor(m => m.LastName,
new { @class = "form- control", id = "lName",
placeholder = "Last Name", type = "text" })
</div>
</div>
<div class="form-group">
<label class="col-xs-4 control-label">Email</label>
<div class="col-xs-8">
@Html.TextBoxFor(m => m.Email,
new { @class = "form-control", id = "email",
placeholder = "Email", type = "email" })
</div>
</div>
<div class="form-group">
<div class="col-xs-12 col-xs-offset-2">
<div class="Recaptcha2">
<table class="">
<tr style="background-color: white;
width: 235px !important">
<td>
<div>
<img src=""
alt="ReCaptchaImage"
id="reCaptchaImage2"
class="img-responsive" />
</div>
</td>
<td>
<div>
<div class="btn-group-vertical">
<span class="btn btn-default btn-xs"
title="Help"><i
class="glyphicon glyphicon-info-sign"></i></span>
<span id="reloadCaptcha2"
class="btn btn-warning btn-sm"
title="Reload captcha image"><i
cl<span class="btn btn-default
btn-xs"><i
class="glyphicon glyphicon-headphones"
title="Speak - development in progress"></i>
</span>
</div>
</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="RecaptchaEnterCode2">
ReCaptcha text enter below
@Html.TextBoxFor(m => m.ReCaptchaCode,
new { @class = "form-control input-sm",
id = "reCaptcha",
placeholder = "Type text ",
type = "text" })
</div>
</td>
<td></td>
</tr>
</table>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-4">
<button type="submit"
class="btn btn-warning" name="Action"
value="ValidateCaptcha2">Validate ReCaptcha</button>
</div>
<div class="col-xs-8">
@if (!string.IsNullOrEmpty(@ViewBag.RechaptchaMessage2))
{
<div class="alert alert- warning">@ViewBag.RechaptchaMessage2</div>
}
</div>
</div>
</div>
</div>
}
@using (Html.BeginForm("ValidateRecaptcha", "Home",
FormMethod.Post, new { id = "mvcForm" }))
{
<div class="col-xs-6">
<h3>Extreme reCAPTCHA</h3>
<div class="form-horizontal well" role="form">
<div class="form-group">
<label class="col-xs-4 control-label">Fisrt Name</label>
<div class="col-xs-8">
@Html.TextBoxFor(m => m.FirstName,
new { @class = "form- control", id = "fName",
placeholder = "First Name", type = "text" })
</div>
</div>
<div class="form-group">
<label class="col-xs-4 control-label">Last Name</label>
<div class="col-xs-8">
@Html.TextBoxFor(m => m.LastName, new {
@class = "form-control", id = "lName",
placeholder = "Last Name", type = "text" })
</div>
</div>
<div class="form-group">
<label class="col-xs-4 control-label">Email</label>
<div class="col-xs-8">
@Html.TextBoxFor(m => m.Email, new {
@class = "form-control", id = "email",
placeholder = "Email", type = "email" })
</div>
</div>
<div class="form-group">
<div class="col-xs-12 col-xs-offset-2">
<div class="Recaptcha">
<table class="">
<tr style="background-color: white;
min-width: 285px !important">
<td>
<div>
<img src="" alt="ReCaptchaImage"
id="reCaptchaImage" class="img-responsive" />
</div>
</td>
<td>
<div>
<div class="btn-group-vertical">
<span class="btn btn-default btn-xs"
title="Help">
<i class="glyphicon glyphicon-info-sign"></i>
</span>
<span id="reloadCaptcha"
class="btn btn-warning btn-sm"
title="Reload captcha image">
<i class="glyphicon glyphicon-refresh"></i>
</span>
<span class="btn btn-default btn-xs"
title="Speak - development in progress">
<i class="glyphicon glyphicon-headphones"></i>
</span>
</div>
</div>
</td>
</tr>
<tr>
<td colspan="2">
<div class="RecaptchaEnterCode">
ReCaptcha text enter below
@Html.TextBoxFor(m => m.ReCaptchaCode,
new { @class = "form-control input-sm",
id = "reCaptcha",
placeholder = "Type text ", type = "text" })
</div>
</td>
<td></td>
</tr>
</table>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-4">
<button type="submit" class="btn btn-primary"
name="Action"
value="ValidateCaptcha1">Validate ReCaptcha</button>
</div>
<div class="col-xs-8">
@if (!string.IsNullOrEmpty(@ViewBag.RechaptchaMessage1))
{
<div class="alert alert-info">@ViewBag.RechaptchaMessage1
</div>
}
</div>
</div>
</div>
</div>
}
</div>
<div class="panel-footer">©
@DateTime.Now.Year - Recaptcha Demo By Shiv</div>
</div>
Step 4
The main part of any of the MVC projects is The Controller, It will facilitate the communication between View and the handler, as I am using handler to create recaptcha image and to process that and make available to the view Controller takes place and can do the work for us.
As you can see the code of controller, it is directly making a call to httphandler
to get the images information and passed as a json result to Index.cshtml view as shown earlier. As I am using the default controller of MVC, the name is HomeController.cs, which we can modify according to our need.
Code of HomeController.cs will look like below:
HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using ReCaptchaDemo.Models;
namespace ReCaptchaDemo.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public JsonResult GetRecaptchaImage()
{
string re = "GenerateRecaptcha.ashx?RcId=" + Guid.NewGuid();
return Json(re);
}
public JsonResult GetRecaptchaImage2()
{
string re = "GenerateRecaptcha2.ashx?RcId=" + Guid.NewGuid();
return Json(re);
}
[HttpPost]
public ActionResult ValidateRecaptcha(Employee employee)
{
string msg;
if (HttpContext.Session != null &&
Convert.ToString(HttpContext.Session["captcha"]) == employee.ReCaptchaCode)
{
msg = "Recaptcha Validation Success";
}
else
{
msg = "Recaptcha Validation Fail";
}
ViewBag.RechaptchaMessage1 = msg;
return View("Index");
}
[HttpPost]
public ActionResult ValidateRecaptcha2(Employee employee)
{
string msg;
if (HttpContext.Session != null &&
Convert.ToString(HttpContext.Session["captcha2"]) ==
employee.ReCaptchaCode)
{
msg = "Recaptcha Validation Success";
}
else
{
msg = "Recaptcha Validation Fail";
}
ViewBag.RechaptchaMessage2 = msg;
return View("Index");
}
}
}
Step 5
Creating Simple Recaptcha
To creat image for recaptcha, I am using a Generic handler to provide the required output as an image in my case.
Generic handler is basically used when the process that runs in response to a request is made to an ASP.NET Web application.
Generic handlers have an extension of ASHX. They're equivalent to custom handlers written in C Sharp or Visual Basic.NET in that they contain classes that fully implement IHttpHandler
. They're convenient in the same way ASPX files are convenient. You simply surf to them and they're compiled automatically.
Just as ASPX files can be compiled on the fly (just-in-time), so can handlers.
For more information about httphandler
, please follow this link:
Some Features of ashx Handler
Dynamic image generation: You can write handlers that return data driven images by creating an ASHX handler that returns image data and then using that URL in your tags.
e.g.<img alt="user's custom icon" src="Icon.ashx?username=bob"></img>
Returning REST-based XML or JSON data to AJAX code on the client.
Custom HTML: Returns totally custom HTML for a page when the ASP.NET Web Forms or MVC framework is too restrictive.
Code of GenerateRecaptcha.ashx will look like below:
GenerateRecaptcha.ashx - Simple ReCaptcha
using System;
using System.Web;
using System.Drawing;
using System.Drawing.Text;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Web.SessionState;
namespace ReCaptchaDemo
{
public class GenerateRecaptcha : IHttpHandler, IRequiresSessionState
{
public void ProcessRequest(HttpContext context)
{
int captchaStr = new Random().Next(6, 12);
context.Session["captcha"] = GenerateRandomString(captchaStr);
const int iHeight = 70;
const int iWidth = 250;
var oRandom = new Random();
int[] aFontEmSizes = { 15, 20, 25, 30 };
string[] aFontNames = { "Comic Sans MS", "Arial",
"Times New Roman", "Georgia", "Verdana", "Geneva" };
FontStyle[] aFontStyles =
{
FontStyle.Bold,
FontStyle.Italic,
FontStyle.Regular,
FontStyle.Strikeout,
FontStyle.Underline
};
HatchStyle[] aHatchStyles =
{
HatchStyle.BackwardDiagonal, HatchStyle.Cross,
HatchStyle.DashedDownwardDiagonal, HatchStyle.DashedHorizontal,
HatchStyle.DashedUpwardDiagonal, HatchStyle.DashedVertical,
HatchStyle.DiagonalBrick, HatchStyle.DiagonalCross,
HatchStyle.Divot, HatchStyle.DottedDiamond, HatchStyle.DottedGrid,
HatchStyle.ForwardDiagonal, HatchStyle.Horizontal,
HatchStyle.HorizontalBrick, HatchStyle.LargeCheckerBoard,
HatchStyle.LargeConfetti, HatchStyle.LargeGrid,
HatchStyle.LightDownwardDiagonal, HatchStyle.LightHorizontal,
HatchStyle.LightUpwardDiagonal, HatchStyle.LightVertical,
HatchStyle.Max, HatchStyle.Min, HatchStyle.NarrowHorizontal,
HatchStyle.NarrowVertical, HatchStyle.OutlinedDiamond,
HatchStyle.Plaid, HatchStyle.Shingle, HatchStyle.SmallCheckerBoard,
HatchStyle.SmallConfetti, HatchStyle.SmallGrid,
HatchStyle.SolidDiamond, HatchStyle.Sphere, HatchStyle.Trellis,
HatchStyle.Vertical, HatchStyle.Wave, HatchStyle.Weave,
HatchStyle.WideDownwardDiagonal, HatchStyle.WideUpwardDiagonal,
HatchStyle.ZigZag
};
string sCaptchaText = context.Session["captcha"].ToString();
var oOutputBitmap = new Bitmap(iWidth, iHeight, PixelFormat.Format24bppRgb);
var oGraphics = Graphics.FromImage(oOutputBitmap);
oGraphics.TextRenderingHint = TextRenderingHint.AntiAlias;
var oRectangleF = new RectangleF(0, 0, iWidth, iHeight);
Brush oBrush = new HatchBrush
(aHatchStyles[oRandom.Next(aHatchStyles.Length - 1)], Color.Gainsboro,
Color.White);
Color.FromArgb((oRandom.Next(100, 255)), (oRandom.Next(100, 255)),
(oRandom.Next(100, 255))), Color.White);
oGraphics.FillRectangle(oBrush, oRectangleF);
var oMatrix = new Matrix();
int i;
for (i = 0; i <= sCaptchaText.Length - 1; i++)
{
oMatrix.Reset();
int iChars = sCaptchaText.Length;
int x = iWidth / (iChars + 1) * i;
const int y = iHeight / 2;
oMatrix.RotateAt(oRandom.Next(-40, 40), new PointF(x, y));
oGraphics.Transform = oMatrix;
oGraphics.DrawString
(
sCaptchaText.Substring(i, 1),
new Font(aFontNames[oRandom.Next(aFontNames.Length - 1)],
aFontEmSizes[oRandom.Next(aFontEmSizes.Length - 1)],
aFontStyles[oRandom.Next(aFontStyles.Length - 1)]),
new SolidBrush(Color.FromArgb
(oRandom.Next(0, 100), oRandom.Next(0, 100), oRandom.Next(0, 100))),
x,
oRandom.Next(10, 40)
);
oGraphics.ResetTransform();
}
context.Response.ContentType = "image/JPEG";
oOutputBitmap.Save(context.Response.OutputStream, ImageFormat.Jpeg);
oOutputBitmap.Dispose();
oGraphics.Dispose();
Console.WriteLine();
context.Response.End();
}
public bool IsReusable
{
get
{
return false;
}
}
public static string GenerateRandomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz";
string result = "";
var rand = new Random();
for (int i = 0; i < length; i++)
{
result += chars[rand.Next(chars.Length)];
}
return result;
}
}
}
Code of GenerateRecaptcha2.ashx will look like below:
GenerateRecaptcha2.ashx - Complex ReCaptcha
using System;
using System.Web;
using System.Drawing;
using System.Drawing.Text;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Web.SessionState;
namespace ReCaptchaDemo
{
public class GenerateRecaptcha2 : IHttpHandler, IRequiresSessionState
{
public void ProcessRequest(HttpContext context)
{
Bitmap objBMP = new Bitmap(200, 60);
Graphics objGraphics = Graphics.FromImage(objBMP);
objGraphics.Clear(Color.White);
objGraphics.TextRenderingHint = TextRenderingHint.AntiAlias;
objGraphics.CompositingQuality = CompositingQuality.HighQuality;
objGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
Font objFont = new Font("Georgia", 26, FontStyle.Italic);
string randomStr = GenerateRandomString(8);
HttpContext.Current.Session.Add("captcha2", randomStr);
objGraphics.DrawString(randomStr, objFont, Brushes.Black, 3, 3);
context.Response.ContentType = "image/JPEG";
objBMP.Save(context.Response.OutputStream, ImageFormat.Jpeg);
objFont.Dispose();
objGraphics.Dispose();
objBMP.Dispose();
}
public bool IsReusable
{
get
{
return false;
}
}
public static string GenerateRandomString(int length)
{
const string chars ="ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz";
string result = "";
var rand = new Random();
for (int i = 0; i < length; i++)
{
result += chars[rand.Next(chars.Length)];
}
return result;
}
}
}
Points of Interest
While implementing this solution, I love to see the power of System.Drawing;
and graphics of .NET assemblies. It is quite interesting, sometimes I used to play a lot with the code supported by those assemblies to create different images and output.