How to implement reCaptcha for ASP.NET MVC?

C#asp.netasp.net Mvc-2

C# Problem Overview


How do I implement reCaptcha in ASP.NET MVC and C#?

C# Solutions


Solution 1 - C#

There are a few great examples:

This has also been covered before in this Stack Overflow question.

NuGet Google reCAPTCHA V2 for MVC 4 and 5

Solution 2 - C#

I have added reCaptcha to a project I'm currently working on. I needed it to use the AJAX API as the reCaptcha element was loaded into the page dynamically. I couldn't find any existing controls and the API is simple so I created my own.

I'll post my code here in case anyone finds it useful.

1: Add the script tag to the master page headers

<script type="text/javascript" src="http://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script>

2: Add your keys to the web.config

<appSettings>
    <add key="ReCaptcha.PrivateKey" value="[key here]" />
    <add key="ReCaptcha.PublicKey" value="[key here]" />
</appSettings>

3: Create the Action Attribute and Html Helper extensions

namespace [Your chosen namespace].ReCaptcha
{
    public enum Theme { Red, White, BlackGlass, Clean }

    [Serializable]
    public class InvalidKeyException : ApplicationException
    {
        public InvalidKeyException() { }
        public InvalidKeyException(string message) : base(message) { }
        public InvalidKeyException(string message, Exception inner) : base(message, inner) { }
    }

    public class ReCaptchaAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var userIP = filterContext.RequestContext.HttpContext.Request.UserHostAddress;

            var privateKey = ConfigurationManager.AppSettings.GetString("ReCaptcha.PrivateKey", "");

            if (string.IsNullOrWhiteSpace(privateKey))
                throw new InvalidKeyException("ReCaptcha.PrivateKey missing from appSettings");

            var postData = string.Format("&privatekey={0}&remoteip={1}&challenge={2}&response={3}",
                                         privateKey,
                                         userIP,
                                         filterContext.RequestContext.HttpContext.Request.Form["recaptcha_challenge_field"],
                                         filterContext.RequestContext.HttpContext.Request.Form["recaptcha_response_field"]);

            var postDataAsBytes = Encoding.UTF8.GetBytes(postData);

            // Create web request
            var request = WebRequest.Create("http://www.google.com/recaptcha/api/verify");
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = postDataAsBytes.Length;
            var dataStream = request.GetRequestStream();
            dataStream.Write(postDataAsBytes, 0, postDataAsBytes.Length);
            dataStream.Close();

            // Get the response.
            var response = request.GetResponse();
           
            using (dataStream = response.GetResponseStream())
            {
                using (var reader = new StreamReader(dataStream))
                {
                    var responseFromServer = reader.ReadToEnd();

                    if (!responseFromServer.StartsWith("true"))
                        ((Controller)filterContext.Controller).ModelState.AddModelError("ReCaptcha", "Captcha words typed incorrectly");
                }
            }
        }
    }

    public static class HtmlHelperExtensions
    {
        public static MvcHtmlString GenerateCaptcha(this HtmlHelper helper, Theme theme, string callBack = null)
        {
            const string htmlInjectString = @"<div id=""recaptcha_div""></div>
<script type=""text/javascript"">
    Recaptcha.create(""{0}"", ""recaptcha_div"", {{ theme: ""{1}"" {2}}});
</script>";

            var publicKey = ConfigurationManager.AppSettings.GetString("ReCaptcha.PublicKey", "");

            if (string.IsNullOrWhiteSpace(publicKey))
                throw new InvalidKeyException("ReCaptcha.PublicKey missing from appSettings");

            if (!string.IsNullOrWhiteSpace(callBack))
                callBack = string.Concat(", callback: ", callBack);

            var html = string.Format(htmlInjectString, publicKey, theme.ToString().ToLower(), callBack);
            return MvcHtmlString.Create(html);
        }
    }
}

4: Add the captcha to your view

@using (Html.BeginForm("MyAction", "MyController"))
{
   @Html.TextBox("EmailAddress", Model.EmailAddress)
   @Html.GenerateCaptcha(Theme.White)
   <input type="submit" value="Submit" />
}

5: Add the attribute to your action

[HttpPost]
[ReCaptcha]
public ActionResult MyAction(MyModel model)
{
   if (!ModelState.IsValid) // Will have a Model Error "ReCaptcha" if the user input is incorrect
      return Json(new { capthcaInvalid = true });

   ... other stuff ...
}

6: Note you will need to reload the captcha after each post even if it was valid and another part of the form was invalid. Use Recaptcha.reload();

Solution 3 - C#

Simple and Complete Solution working for me. Supports ASP.NET MVC 4 and 5 (Supports ASP.NET 4.0, 4.5, and 4.5.1)

Step 1: Install NuGet Package by "Install-Package reCAPTCH.MVC"

Step 2: Add your Public and Private key to your web.config file in appsettings section

<appSettings>
	<add key="ReCaptchaPrivateKey" value=" -- PRIVATE_KEY -- " />
	<add key="ReCaptchaPublicKey" value=" -- PUBLIC KEY -- " />
</appSettings>	

You can create an API key pair for your site at https://www.google.com/recaptcha/intro/index.html and click on Get reCAPTCHA at top of the page

Step 3: Modify your form to include reCaptcha

@using reCAPTCHA.MVC
@using (Html.BeginForm())
{
    @Html.Recaptcha()
    @Html.ValidationMessage("ReCaptcha")
    <input type="submit" value="Register" />
}

Step 4: Implement the Controller Action that will handle the form submission and Captcha validation

[CaptchaValidator(PrivateKey = "your private reCaptcha Google Key",ErrorMessage = "Invalid input captcha.",RequiredMessage = "The captcha field is required.")]
public ActionResult MyAction(myVM model)
{
	if (ModelState.IsValid) //this will take care of captcha
    {
	}
}

	

OR

public ActionResult MyAction(myVM model, bool captchaValid)
{
	if (captchaValid) //manually check for captchaValid 
    {
	}
}

Solution 4 - C#

An async version for MVC 5 (i.e. avoiding ActionFilterAttribute, which is not async until MVC 6) and reCAPTCHA 2

ExampleController.cs

public class HomeController : Controller
{
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> ContactSubmit(
        [Bind(Include = "FromName, FromEmail, FromPhone, Message, ContactId")]
        ContactViewModel model)
    {
        if (!await RecaptchaServices.Validate(Request))
        {
            ModelState.AddModelError(string.Empty, "You have not confirmed that you are not a robot");
        }
        if (ModelState.IsValid)
        {
           ...
		   

ExampleView.cshtml

@model MyMvcApp.Models.ContactViewModel

@*This is assuming the master layout places the styles section within the head tags*@
@section Styles {
	@Styles.Render("~/Content/ContactPage.css")
	<script src='https://www.google.com/recaptcha/api.js'></script>
}

@using (Html.BeginForm("ContactSubmit", "Home",FormMethod.Post, new { id = "contact-form" }))
{
	@Html.AntiForgeryToken()
	...
	<div class="form-group">
	  @Html.LabelFor(m => m.Message) 
	  @Html.TextAreaFor(m => m.Message, new { @class = "form-control", @cols = "40", @rows = "3" })
	  @Html.ValidationMessageFor(m => m.Message)
	</div>
	  
	<div class="row">
	  <div class="g-recaptcha" data-sitekey='@System.Configuration.ConfigurationManager.AppSettings["RecaptchaClientKey"]'></div>
	</div>

	<div class="row">
	  <input type="submit" id="submit-button" class="btn btn-default" value="Send Your Message" />
	</div>
}

RecaptchaServices.cs

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Web;
using System.Configuration;
using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json;
using System.Runtime.Serialization;

namespace MyMvcApp.Services
{
	public class RecaptchaServices
	{
		//ActionFilterAttribute has no async for MVC 5 therefore not using as an actionfilter attribute - needs revisiting in MVC 6
		internal static async Task<bool> Validate(HttpRequestBase request)
		{
            string recaptchaResponse = request.Form["g-recaptcha-response"];
            if (string.IsNullOrEmpty(recaptchaResponse))
            {
                return false;
            }
			using (var client = new HttpClient { BaseAddress = new Uri("https://www.google.com") })
			{
				client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
				var content = new FormUrlEncodedContent(new[]
				{
					new KeyValuePair<string, string>("secret", ConfigurationManager.AppSettings["RecaptchaSecret"]),
					new KeyValuePair<string, string>("response", recaptchaResponse),
					new KeyValuePair<string, string>("remoteip", request.UserHostAddress)
				});
				var result = await client.PostAsync("/recaptcha/api/siteverify", content);
				result.EnsureSuccessStatusCode();
				string jsonString = await result.Content.ReadAsStringAsync();
				var response = JsonConvert.DeserializeObject<RecaptchaResponse>(jsonString);
				return response.Success;
			}
		}

		[DataContract]
		internal class RecaptchaResponse
		{
			[DataMember(Name = "success")]
			public bool Success { get; set; }
			[DataMember(Name = "challenge_ts")]
			public DateTime ChallengeTimeStamp { get; set; }
			[DataMember(Name = "hostname")]
			public string Hostname { get; set; }
			[DataMember(Name = "error-codes")]
			public IEnumerable<string> ErrorCodes { get; set; }
		}

	}
}

web.config

<configuration>
  <appSettings>
	<!--recaptcha-->
	<add key="RecaptchaSecret" value="***secret key from https://developers.google.com/recaptcha***" />
    <add key="RecaptchaClientKey" value="***client key from https://developers.google.com/recaptcha***" />
  </appSettings>
</configuration>

Solution 5 - C#

Step 1: Client site integration

Paste this snippet before the closing </head> tag on your HTML template:

<script src='https://www.google.com/recaptcha/api.js'></script>

Paste this snippet at the end of the <form> where you want the reCAPTCHA widget to appear:

<div class="g-recaptcha" data-sitekey="your-site-key"></div>

Step 2: Server site integration

When your users submit the form where you integrated reCAPTCHA, you'll get as part of the payload a string with the name "g-recaptcha-response". In order to check whether Google has verified that user, send a POST request with these parameters:

URL : https://www.google.com/recaptcha/api/siteverify

secret : your secret key

response : The value of 'g-recaptcha-response'.

Now in action of your MVC app:

// return ActionResult if you want
    public string RecaptchaWork()
    {
        // Get recaptcha value
        var r = Request.Params["g-recaptcha-response"];
        // ... validate null or empty value if you want
        // then
        // make a request to recaptcha api
        using (var wc = new WebClient())
        {
            var validateString = string.Format(
                "https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}",
               "your_secret_key",    // secret recaptcha key
               r); // recaptcha value
             // Get result of recaptcha
            var recaptcha_result = wc.DownloadString(validateString);
            // Just check if request make by user or bot
            if (recaptcha_result.ToLower().Contains("false"))
            {
                 return "recaptcha false";
            }
        }
        // Do your work if request send from human :)
    }

Solution 6 - C#

I've successfully implemented ReCaptcha in the following way.
note: this is in VB, but can easily be converted

1] First grab a copy of the reCaptcha library

2] Then build a custom ReCaptcha HTML Helper

    ''# fix SO code coloring issue.
    <Extension()>
    Public Function reCaptcha(ByVal htmlHelper As HtmlHelper) As MvcHtmlString
        Dim captchaControl = New Recaptcha.RecaptchaControl With {.ID = "recaptcha",
                                                                  .Theme = "clean",
                                                                  .PublicKey = "XXXXXX",
                                                                  .PrivateKey = "XXXXXX"}
        Dim htmlWriter = New HtmlTextWriter(New IO.StringWriter)
        captchaControl.RenderControl(htmlWriter)
        Return MvcHtmlString.Create(htmlWriter.InnerWriter.ToString)
    End Function

3] From here you need a re-usable server side validator

Public Class ValidateCaptchaAttribute : Inherits ActionFilterAttribute
    Private Const CHALLENGE_FIELD_KEY As String = "recaptcha_challenge_field"
    Private Const RESPONSE_FIELD_KEY As String = "recaptcha_response_field"

    Public Overrides Sub OnActionExecuting(ByVal filterContext As ActionExecutingContext)

        If IsNothing(filterContext.HttpContext.Request.Form(CHALLENGE_FIELD_KEY)) Then
            ''# this will push the result value into a parameter in our Action
            filterContext.ActionParameters("CaptchaIsValid") = True
            Return
        End If

        Dim captchaChallengeValue = filterContext.HttpContext.Request.Form(CHALLENGE_FIELD_KEY)
        Dim captchaResponseValue = filterContext.HttpContext.Request.Form(RESPONSE_FIELD_KEY)

        Dim captchaValidtor = New RecaptchaValidator() With {.PrivateKey = "xxxxx",
                                                                       .RemoteIP = filterContext.HttpContext.Request.UserHostAddress,
                                                                       .Challenge = captchaChallengeValue,
                                                                       .Response = captchaResponseValue}

        Dim recaptchaResponse = captchaValidtor.Validate()

        ''# this will push the result value into a parameter in our Action
        filterContext.ActionParameters("CaptchaIsValid") = recaptchaResponse.IsValid

        MyBase.OnActionExecuting(filterContext)
    End Sub

above this line is reusable ONE TIME code

below this line is how easy it is to implement reCaptcha over and over

Now that you have your re-usable code... all you need to do is add the captcha to your View.

<%: Html.reCaptcha %>

And when you post the form to your controller...

    ''# Fix SO code coloring issues
    <ValidateCaptcha()>
    <AcceptVerbs(HttpVerbs.Post)>
    Function Add(ByVal CaptchaIsValid As Boolean, ByVal [event] As Domain.Event) As ActionResult


        If Not CaptchaIsValid Then ModelState.AddModelError("recaptcha", "*")


        '#' Validate the ModelState and submit the data.
        If ModelState.IsValid Then
            ''# Post the form
        Else
            ''# Return View([event])
        End If
    End Function

Solution 7 - C#

Extending Magpie's answer, here is the code for action filter which I use in my project.

It works with ASP Core RC2!

public class ReCaptchaAttribute : ActionFilterAttribute
{
	private readonly string CAPTCHA_URL = "https://www.google.com/recaptcha/api/siteverify";
	private readonly string SECRET = "your_secret";

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
		try
		{
			// Get recaptcha value
			var captchaResponse = filterContext.HttpContext.Request.Form["g-recaptcha-response"];

			using (var client = new HttpClient())
			{
				var values = new Dictionary<string, string>
				{
					{ "secret", SECRET },
					{ "response", captchaResponse },
					{ "remoteip", filterContext.HttpContext.Request.HttpContext.Connection.RemoteIpAddress.ToString() }
				};


				var content = new FormUrlEncodedContent(values);

				var result = client.PostAsync(CAPTCHA_URL, content).Result;

				if (result.IsSuccessStatusCode)
				{
					string responseString = result.Content.ReadAsStringAsync().Result;

					var captchaResult = JsonConvert.DeserializeObject<CaptchaResponseViewModel>(responseString);

					if (!captchaResult.Success)
					{
						((Controller)filterContext.Controller).ModelState.AddModelError("ReCaptcha", "Captcha not solved");
					}
				} else
				{
					((Controller)filterContext.Controller).ModelState.AddModelError("ReCaptcha", "Captcha error");
				}
			}

		}
		catch (System.Exception)
		{
			((Controller)filterContext.Controller).ModelState.AddModelError("ReCaptcha", "Unknown error");
		}
	}
}

And use it in your code like

[ReCaptcha]
	public IActionResult Authenticate()
	{

		if (!ModelState.IsValid)
		{
			return View(
				"Login",
				new ReturnUrlViewModel
				{
					ReturnUrl = Request.Query["returnurl"],
					IsError = true,
					Error = "Wrong reCAPTCHA"
				}
			);
		}

Solution 8 - C#

For anybody else looking, here is a decent set of steps. http://forums.asp.net/t/1678976.aspx/1

Don't forget to manually add your key in OnActionExecuting() like I did.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionxportView Question on Stackoverflow
Solution 1 - C#George StockerView Answer on Stackoverflow
Solution 2 - C#MagpieView Answer on Stackoverflow
Solution 3 - C#MojiView Answer on Stackoverflow
Solution 4 - C#BrentView Answer on Stackoverflow
Solution 5 - C#Thinh VuView Answer on Stackoverflow
Solution 6 - C#Chase FlorellView Answer on Stackoverflow
Solution 7 - C#Dmytro BogatovView Answer on Stackoverflow
Solution 8 - C#PatView Answer on Stackoverflow