reCaptcha 2.0 Server Side Validation





3.00/5 (2 votes)
Implements server side validation of Google's revamped reCaptcha and includes the ability to inspect any returned error codes
Introduction
Google recently updated their reCaptcha service to 2.0, this brought a large UI refresh and a new approach designed to make the user experience simpler and reduce the number of challenges that the user will have to answer. This new update also changed some of the ways in which reCaptcha is implemented server-side, with the previous APIs seemingly now no more.
Background
The new reCaptcha is, as much as possible, self contained, it takes 2 lines of code to implement client-side, a script reference and a DIV
with your site key and that is it. Should the user be presented with a challenge, all the validation of their response is handled within the captcha control itself. Of course, it is important to remember that the 'g-recaptcha-response
' that reCaptcha sends when the form is submitted could be forged/malformed by a malicious user so it is important to carry out secondary validation once the request hits the server.
When looking round for how to do this, there are several very good examples already out there, this one being a very good source if you aren't concerned about the error-codes that the captcha returns:
I however wanted to try and take all these approaches a step further and tailor the error messages that the user would be shown depending upon the error-code, if any, returned from Google.
The Code
The first thing you will need is a simple class to hold the response that we'll be getting from the Google Servers, in this case, I created a GoogleResponse
class with the 'Success
' and 'ErrorCodes
' fields.
[DataContract]
public class GoogleResponse
{
[DataMember(Name = "success")]
public bool Success { get; set; }
[DataMember(Name = "error-codes")]
public List<string> ErrorCodes { get; set; }
}
This class also makes use of DataContract
from System.Runtime.Serialization
. This is important if you want to inspect the error-codes as the standard JSON mapping won't be able to map error-codes from the response to ErrorCodes
in our class. You can see that the [DataMember(Name="")]
is where we define what JSON filed this specific field will map to. Another point to note is that multiple possible string
s could be returned in the error-codes, so you will need to make sure that you use List<string>
, if you just use string
, exceptions will start being thrown. Success can be either bool
or string
, I stuck with bool
simply because it saves on a conversion step later on.
private bool isCaptchaValid()
{
string response = Request["g-recaptcha-response"];
bool valid = false;
var requestString = string.Format
("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}",
[[SECRETKEY]], response);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestString);
try
{
using (WebResponse captchaResponse = request.GetResponse())
{
DataContractJsonSerializer serializer =
new DataContractJsonSerializer(typeof(GoogleResponse));
GoogleResponse gResponse =
serializer.ReadObject(captchaResponse.GetResponseStream()) as GoogleResponse;
valid = gResponse.Success;
if(!valid)
{
if(gResponse.ErrorCodes.Count > 0)
{
switch(gResponse.ErrorCodes[0].ToLower())
{
case("missing-input-response"):
_captchaError = "Please provide a response to the captcha";
break;
case("invalid-input-response"):
_captchaError = "Invalid captcha response, please try again";
break;
default:
_captchaError = "An error was encountered
while validating the captcha. Please try again"
break;
}
}
else
{
_captchaError = "We're sorry, an unknown error has occured,
please reload the page and try again";
}
}
}
return valid;
}
catch (WebException ex)
{
//Log the error if necessary, ignore or throw to be handled at a higher level
throw ex;
}
}
The method above takes care of the validation. It pulls the g-recaptcha-response
out from the request and passes into a formatted string
, along with the secret key that you generate when you download reCaptcha. This is then wrapped up and sent to Google for verification and once we get the response, this is where the process differs slightly from other approaches I have seen before.
We need to make use of the DataContractJsonSerializer
, this will allow us to make use of our decorated class that we created at the start. The ReadObject()
takes a stream so unlike other approaches, we can cut the step of converting the response to a string entirely and just pass the raw response stream from the web request.
We now have an object that contains the success/fail response from Google and on top of this, we will also have a list of errors that occured, if applicable. To keep things simple, I'm only concerned with the first error listed, since the way Google documents the errors that could be returned seems to suggest that, most of the time at least, they will be mutually exclusive. The list of possible error responses can be found at the following link:
Using a switch
statement, a class level variable is set to allow the error message to be referenced later on when the error messages are being constructed to send back to the UI.
History
- 18/12/2014 - Initial version