Tuesday 14 October 2014

User friendly CAPTCHA for Asp.Net MVC

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
CaptchaController.cs
        public ActionResult CaptchaImage(string prefix, bool noisy = true)
        {
            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
Models.cs
    public class SubscribeModel
    {
        //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.
Index.cshtml
@*form specific fields*@ <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.
CaptchaController.cs
[HttpPost] 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");
}

C# LINQ Joins With SQL

There are  Different Types of SQL Joins  which are used to query data from more than one database tables. In this article, you will learn a...