Stop spam, read books – ReCaptcha and ASP.NET MVC


You have a page and you want only human beings to register or change its contents? What to do? Some would say that you should implement some extremely complex testing “thing” that will cost you more than you will gain with it. The cheap and proven alternative is to take a CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart). One of the more popular and easy to use is reCaptcha.

So the question now is how to use it in you new and shinny ASP.NET MVC application? This is quite easy and straight forward.

1. Register with reCaptcha

This step you can not avoid. You will have to register with them to receive your private and public key.

2. Download the reCaptcha .net library

The library has has “big” bug and we will only use the validation part of it. The rendering of the captcha will be handled by our code. To make it short and painless you can get the library here.

3. Reference the library in your UI project

I hope no further explanation is needed here.

4. Create a HTML helper to render the reCaptcha in your form

This is where we deviate the most from the other. Why? Because I needed the captcha to be displayed in German. This was a problem because the control renders a little to much. In theory the reCaptcha display is configured with setting a properties in a JS hashtable.

 
<script>
  var RecaptchaOptions = {
  lang : 'de',
  tabindex : 2
};
</script> 

Sadly the control renders this script block with some default values and you can only set the tabindex and theme, all the other configuration options are not available.

So what to do? Well I took a look at the source code and take out the good parts. To make the whole thing even more reusable I packed into a extension for the Html object.

public static string Generatecaptcha(this HtmlHelper helper)
{
	HtmlTextWriter writer = new HtmlTextWriter( new StringWriter());

	writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
	writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
	writer.AddAttribute(HtmlTextWriterAttribute.Src, GenerateChallengeUrl(false), false);
	writer.RenderBeginTag(HtmlTextWriterTag.Script);
	writer.RenderEndTag();
	writer.RenderBeginTag(HtmlTextWriterTag.Noscript);
	writer.Indent++;
	writer.AddAttribute(HtmlTextWriterAttribute.Src, GenerateChallengeUrl(true), false);
	writer.AddAttribute(HtmlTextWriterAttribute.Width, "500");
	writer.AddAttribute(HtmlTextWriterAttribute.Height, "300");
	writer.AddAttribute("frameborder", "0");
	writer.RenderBeginTag(HtmlTextWriterTag.Iframe);
	writer.RenderEndTag();
	writer.RenderBeginTag(HtmlTextWriterTag.Br);
	writer.RenderEndTag();
	writer.AddAttribute(HtmlTextWriterAttribute.Name, "recaptcha_challenge_field");
	writer.AddAttribute(HtmlTextWriterAttribute.Rows, "3");
	writer.AddAttribute(HtmlTextWriterAttribute.Cols, "40");
	writer.RenderBeginTag(HtmlTextWriterTag.Textarea);
	writer.RenderEndTag();
	writer.AddAttribute(HtmlTextWriterAttribute.Name, "recaptcha_response_field");
	writer.AddAttribute(HtmlTextWriterAttribute.Value, "manual_challenge");
	writer.AddAttribute(HtmlTextWriterAttribute.Type, "hidden");
	writer.RenderBeginTag(HtmlTextWriterTag.Input);
	writer.RenderEndTag();
	writer.Indent--;
	writer.RenderEndTag();

	return writer.InnerWriter.ToString();
}

private static string GenerateChallengeUrl(bool noScript)
{
	StringBuilder builder = new StringBuilder();
	builder.Append("http://api.recaptcha.net");
	builder.Append(noScript ? "/noscript?" : "/challenge?");
	builder.AppendFormat("k={0}", SecuritySettings.ReCaptchaPublicKey);
	return builder.ToString();
}

Ok that’s a lot of code but do not worry this was the hardest part. From now on it will get more simple.

5. Validate the captcha on form submit

Now that we have a way to display the captcha we need some sort of mechanism to validate if there a human being on the other site of the form.

For this we will create a ActionFilter to handle this.

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
	string chalengeValue = filterContext.HttpContext.Request.Form[recaptcha_challenge_field];
	string responseValue = filterContext.HttpContext.Request.Form[recaptcha_response_field];
	string remoteUserIP = filterContext.HttpContext.Request.UserHostAddress;
	RecaptchaValidator validator = new RecaptchaValidator
									   {
										   Challenge = chalengeValue,
										   PrivateKey = [your private key],
										   RemoteIP = remoteUserIP,
										   Response = responseValue
									   };

	filterContext.ActionParameters["captchaValid"] = validator.Validate().IsValid;

	base.OnActionExecuting(filterContext);
}

This will add a parameter to your controller method called captchaValid. If this is true then we can assume that a human being is trying to do something. If not well…

6. Use it

Well that’s that. All that is left to do is to use it in your views.

<%= Html.Generatecaptcha() %> 

Well hope that will help some poor developer that would be otherwise frustrated just like yours truly.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s