Recaptcha Error - "Value cannot be null. Parameter name: Challenge

In one web application that uses the popular Recaptcha control to prevent spam we were receiving a decent number of exceptions related to invalid parameters. The error was "Value cannot be null. Parameter name: Challenge". I'm going to explain this error and how to go about fixing it.

In my searches on the web looking for a solution to this problem, I found many instances where it was recommended that the Recaptcha control's SkipRecaptcha property be set to false. This disables the validation and thus prevents the problem. However, what if you're still getting this problem when you WANT Recaptcha to be enabled... that's a problem so I dug a little deeper.

Utilizing a custom written application error handling module, I was able to track the exceptions and the post data being submitted when an error occurs. This resulted in output similar to the following:

Exception Info - Depth 1
Message: Exception of type 'System.Web.HttpUnhandledException' was thrown.
Source: System.Web
Target Site: Boolean HandleError(System.Exception)
Trace:

   at System.Web.UI.Page.HandleError(Exception e)
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
   at System.Web.UI.Page.ProcessRequest()
   at System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context)
   at System.Web.UI.Page.ProcessRequest(HttpContext context)
   at ASP.editpost_aspx.ProcessRequest(HttpContext context)
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

Exception Info - Depth 2
Message: Value cannot be null. Parameter name: Challenge
Source: Recaptcha
Target Site: Recaptcha.RecaptchaResponse Validate()
Trace:

   at Recaptcha.RecaptchaValidator.Validate()
   at Recaptcha.RecaptchaControl.Validate()
   at System.Web.UI.Page.Validate()
   at System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData)
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

Generally, the exception message means that during the validation processing of the page, a control is attempting to be validated but fails because the required post data could not be found. In this case, the Recaptcha control is attempting to validate, but the form information is not found in the Request's posted data. To me this highlights two problems:

1) For some odd reason, not all of the post data was being submitted to the server.

2) The ASP.NET recaptcha control has a bug in that it should not be throwing an exception in the event that the Recaptcha form data is not found. It should be marked as invalid. My reasoning explained below.

Now, the reason I say point 2 is a bug is because it should be expected that not all post data is submitted. After reviewing the errors that were being generated it was clear that not all post data was being submitted. This error was occurring on a large number of different browsers and operating systems. My investigation had me looking through the page and seeing the information that was being processed and the typical point in which it was failing.

THE SOLUTION

After reviewing the page, I found that the Recaptcha control was the last form element. Prior to it and a few other fields were File Upload inputs. The user had the option to upload information to the server. If the user "reclicked" the submit button while the page was uploading, it would prevent all form data from being submitted. Typically, this would occur while uploading files, and thus the Recaptcha information would not be processed causing our fun exception.

Therefore, it is an expected condition that not all required post data will reach the server and the Recaptcha control should be marked as invalid if the required parameter is not found.

The work around for this is rather simple. In order to prevent the user from double clicking or reclicking the Submit button, we'll simply hide it using some JavaScript.

Your first reaction may be to "disable" the submit button. But ASP.NET will not properly process your page if the submit button is disabled.

First the HTML:

<div id="submitbuttons">
     <asp:Button ID="SaveButton" runat="server" Text="Create Post" OnClick="SaveButton_Click" OnClientClick="return formsubmit();"></asp:Button>                                                                                                        
</div>
<div id="submitmessage" style="display:none; font-weight: bold; margin: 1em;">
     Submitting, please wait...
</div>

Now for the JavaScript:

<script type="text/javascript">
    function formsubmit() {
        document.getElementById("submitbuttons").style.display = 'none';
        document.getElementById("submitmessage").style.display = 'block';            
        return true;
    }
</script>

Voila, this will keep your users from resubmitting information during the file upload.

Lastly, you can create exception handling in the page to look for exceptions of the proper type and message.

RECAPTCHA FIX

Since Recaptcha is open source, I modified the RecaptchaValidator class (RecaptchaValidator.cs) to correctly handle null references. Now instead of throwing NullArgumentExceptions it performs a check using the String.IsNullOrEmpty method.

Previous Code:

void CheckNotNull(object obj, string name)
{
    if (obj == null)
    {
        throw new ArgumentNullException(name);
    }
}

public RecaptchaResponse Validate()
{
    CheckNotNull(PrivateKey, "PrivateKey");
    CheckNotNull(RemoteIP, "RemoteIp");
    CheckNotNull(Challenge, "Challenge");
    CheckNotNull(Response, "Response");

    if (challenge == "" || response == "") {
        return RecaptchaResponse.InvalidSolution;
    }

New Code:

public RecaptchaResponse Validate()
{        
    if (string.IsNullOrEmpty(Challenge) ||
        string.IsNullOrEmpty(Response) ||
        string.IsNullOrEmpty(RemoteIP) ||
        string.IsNullOrEmpty(PrivateKey)) {
        return RecaptchaResponse.InvalidSolution;
    }

You can download the Recaptcha zip below which contains the updated Binary. I'll also be submitting this to the Recaptcha Google Code site as a patch.

AttachmentSize
recaptcha-dotnet.zip91.83 KB

Comments

#1 Anonymous

Can't find the download link on the page.

Thanks in advance.

#2 khuang

Sorry, I didn't double check the permissions for file uploads. Its there now :)

Recent comments