In this post I will show you how to add CAPTCHA functionality to a html form in an Asp.Net MVC 4 project. My goal is to make the CAPTCHA problem easy enough for all to solve, like a simple sum operation, and easier to read then the standard CAPTCHA text. An easy to read image is more vulnerable to smart bots that have an ORC system but I prefer to scare less clients then to provide the strongest anti-bot protection. And one more feature, when clicked the image should, change giving the users a new chance to respond correctly.
Implementing CAPTCHA in C# and MVC 4 takes these steps:
- Create an Action that returns a CAPTCHA image and stores in the user session the right answer
- Add to your Model a string property named Captcha
- Add to your View the textbox for Captcha and the image placeholder
- Validate answer inside your own Action
Render CAPTCHA image
{
var rand = new Random((int)DateTime.Now.Ticks);
//generate new question
int a = rand.Next(10, 99);
int b = rand.Next(0, 9);
var captcha = string.Format("{0} + {1} = ?", a, b);
//store answer
Session["Captcha" + prefix] = a + b;
//image stream
FileContentResult img = null;
using (var mem = new MemoryStream())
using (var bmp = new Bitmap(130, 30))
using (var gfx = Graphics.FromImage((Image)bmp))
{
gfx.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
gfx.SmoothingMode = SmoothingMode.AntiAlias;
gfx.FillRectangle(Brushes.White, new Rectangle(0, 0, bmp.Width, bmp.Height));
//add noise
if (noisy)
{
int i, r, x, y;
var pen = new Pen(Color.Yellow);
for (i = 1; i < 10; i++)
{
pen.Color = Color.FromArgb(
(rand.Next(0, 255)),
(rand.Next(0, 255)),
(rand.Next(0, 255)));
r = rand.Next(0, (130 / 3));
x = rand.Next(0, 130);
y = rand.Next(0, 30);
gfx.DrawEllipse(pen, x – r, y – r, r, r);
}
}
//add question
gfx.DrawString(captcha, new Font("Tahoma", 15), Brushes.Gray, 2, 3);
//render as Jpeg
bmp.Save(mem, System.Drawing.Imaging.ImageFormat.Jpeg);
img = this.File(mem.GetBuffer(), "image/Jpeg");
}
return img;
}
If you want to use multiple CAPTCHAs you can use the prefix to store the answer for each form. Much can be improved regarding the rendered image, for example I could use different font and size for each number in the equation, replace the noise with text distortion.
Include CAPTCHA validator in Model and View
{
//model specific fields
[Required]
[Display(Name = "How much is")]
public string Captcha { get; set; }
}
In the View, beside a label, textbox and validator span you’ll need to add an image placeholder for the CAPTCHA.
<div class="editor-label">
@Html.LabelFor(model => model.Captcha)
<a href="@Url.Action("Index")">
<img alt="Captcha" src="@Url.Action("CaptchaImage")" style="" />
</a>
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Captcha)
@Html.ValidationMessageFor(model => model.Captcha)
</div>
Validate CAPTCHA on the server side
Inside your post action where the form submits you can validate the answer by comparing with the session value.
public ActionResult Index(SubscribeModel model)
{
//validate captcha
if (Session["Captcha"] == null || Session["Captcha"].ToString() != model.Captcha)
{
ModelState.AddModelError("Captcha", "Wrong value of sum, please try again.");
//dispay error and generate a new captcha
return View(model);
}
return RedirectToAction("ThankYouPage");
}
Instead of validating the CAPTCHA on post, you can use an Ajax call to validate and refresh the image asynchronous, more details on how you can send forms data over Ajax read this blog post.
Prerequisites & Download
In order to easily install all the prerequisites you can use the Web Platform Installer version 4.
Very nice post!
I will try to use your approach on captcha, in the past i’ve used math generated captcah as well, but just printed the numbers.
You could also enhance this by having randomizing the equation type as well:
string eq = “+-*”;
Random r = new Random();
string op = r.Next(0,2);
but be carefull not to stress the users out with long calculus.
Cheers,
Alex
I would randomizing the equation with + and – only, thanks.
nice
Download source code is broken link!
The download link is now working, sorry for the inconvenience.
Use cookies instead of session variables. avoid denial of service attacks – DOS.
I would avoid cookies too. I created this project a while ago that uses hashing.
http://xcaptcha.codeplex.com/
Thanks.
someone wrote there that it is easily hackable.
Instead of have requiring a postback for getting a new image you could simply update the image source using jQuery. So your
becomes
and you have a JavaScript function of
function getCap() {
$(“#cap”).attr(‘src’, ‘@Url.Action(“CaptchaImage”)?’ + new Date().getTime());
}
Now clicking the image simply updates the image itself, without a post back. Note that the addition of the newDate().getTime() prevents IE from caching the image.
Arg, it stripped the code. I was trying to say replace the href around the image:
a href=”@Url.Action(“Index”)”
with
a href=”#” onclick=”javascript:getCap();”
A nice improvement, thanks for your comments.
can we refresh captcha image
So simple. Love it! Thanks for posting this.
Hi, is there a .NET web forms version of this as well?
Thanks so much for posting this. It was just what I was looking for. I have used reCaptcha and other variations in the past, but in my opinion the image based versions with just letters are getting harder and harder to read because they need to be so distorted to make them secure. This will be much easier for my users. That is unless they can’t do simple math :)
Hi Stefan, my method captcha is running twice, any idea?
When you put a breakpoint in CaptchaImage action does it enter twice?
I have the same problems with the method captcha. It being called twice for every action.
Very Nice and Functional Captcha Thanks