ASP.NET jQuery Validation CAPTCHA C# Code Example
This example project shows how to integrate BotDetect ASP.NET CAPTCHA validation with jQuery Validation client-side form validation.
First Time Here?
Check the BotDetect ASP.NET WebForms Captcha Quickstart for key integration steps.
Client-side validation is not secure by itself (it can be bypassed trivially by bots that don't execute JavaScript at all), so the example shows how the protected form action must always be secured by server-side CAPTCHA validation first, and uses client-side validation only to improve the user experience.
Download the BotDetect ASP.NET CAPTCHA Generator archive to run this example- C#
- VB.NET
Within this page, the root folder of the extracted archive is referred as the <BDC-DIR>
.
This example is in the <BDC-DIR>/lgcy-on-lgcy/examples/t_api-captcha-webforms-api_basics-unobtrusive/csharp/
folder; and contains the following files:
Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head id="Head1" runat="server"> <title>BotDetect ASP.NET CAPTCHA jQuery Validation Example</title> <link type="text/css" rel="Stylesheet" href="StyleSheet.css" /> <script type="text/javascript" src="js/jquery-1.7.2.min.js"></script> <script type="text/javascript" src="js/jquery.validate.min.js"></script> </head> <body> <form id="form1" runat="server"> <h1>BotDetect ASP.NET CAPTCHA jQuery Validation Example</h1> <fieldset> <legend>CAPTCHA Validation</legend> <p class="prompt"> <label for="CaptchaCodeTextBox">Retype the characters from the picture:</label> </p> <BotDetect:WebFormsCaptcha ID="ExampleCaptcha" runat="server" /> <div class="validationDiv"> <!-- add CssClass name used to set up jQuery field validation --> <asp:TextBox ID="CaptchaCodeTextBox" runat="server" CssClass="captchaVal"></asp:TextBox> <br /> <asp:Button ID="ValidateCaptchaButton" runat="server" /> </div> <asp:Label ID="CaptchaCorrectLabel" AssociatedControlID="CaptchaCodeTextBox" runat="server" CssClass="correct"> </asp:Label> <asp:Label ID="CaptchaIncorrectLabel" AssociatedControlID="CaptchaCodeTextBox" runat="server" CssClass="incorrect"></asp:Label> </fieldset> <script type="text/javascript" src="js/validationRules.js"></script> </form> </body> </html>
The input form code is almost exactly the same as in the basic Captcha example. The only differences are that we add a custom CssClass
attribute to the Captcha code input textbox, and that we must include jQuery, the jQuery.validate plugin, and the script containing our custom validation rules. To make the example work in IE 6, we have to load the jQuery includes before Captcha markup.
The custom Css class value is used to ensure we can reliably identify the Captcha textbox from client-side code. Since ASP.NET controls don't necessarily have predictable client-side identifiers (depending on the
ClientIDMode
setting used, whether the control is placed within a naming container, a data-bound repeater etc.), a unique Css class is used instead.
js\validationRules.js
$(document).ready(function () { $("#form1").validate({ // the Captcha input must only be validated when the whole code string is // typed in, not after each individual character (onkeyup must be false) onkeyup: false, // since the example form only has one input element, it doesn't make sense // to validate it immediately after it loses focus onfocusout: false, // customize client-side error display elements errorClass: "incorrect", validClass: "correct", errorElement: "label", // always reload the Captcha image if remote validation failed, // since it will not be usable any more (a failed validation attempt // removes the attempted code for necessary Captcha security showErrors: function (errorMap, errorList) { this.defaultShowErrors(); for (var i = 0; i < errorList.length; i++) { var element = errorList[i].element; var message = errorList[i].message; // check element css class and does the error message match remote // validation failure if (element.className.match(/captchaVal/) && message === this.settings.messages[element.id].remote) { element.Captcha.ReloadImage(); } } } }); // add validation rules by CSS class, so we don't have to know the // exact client id of the Captcha code textbox $(".captchaVal").rules('add', { required: true, remote: $(".captchaVal").get(0).Captcha.ValidationUrl, messages: { required: 'Required (client)', remote: 'Incorrect (client)' } }); });
To set up jQuery validation of a textbox taking Captcha code input, we do the following:
- Add validation rules specifying it as a required field, that also needs to be validated remotely when a value is entered; define error messages shown when these validation rules are triggered
- Disable
onkeyup
validation of the Captcha code, since we must validate the whole code and not the individual characters - Reload the Captcha image whenever remote Captcha validation fails
CAPTCHA jQuery Validation Rules and Error Messages
Since we're not using the input textbox Id for jQuery validation rules, we need to add them dynamically after the default validator has been initialized; that's why they're located after the $("#form1").validate({
call.
The expression for the remote Captcha validation Url can easily be read looking at dot-separated segments from right to left:
- The
ValidationUrl
property is part of the BotDetect client-side API, and is unique for each Captcha instance. - The client-side BotDetect object can always be accessed through the
Captcha
property of the Captcha code textbox. For this custom property to be assigned, the textbox must be registered during server-side Captcha object initialization, through theUserInputID
property (as has been done in Default.aspx.cs code below). - We use the standard jQuery selector to access the textbox by Css class, and the
.get(0)
function call to get the underlying DOM element. This is needed because BotDetect adds the customCaptcha
property to the DOM element directly, and the jQuery wrapper element returned by the jQuery selector doesn't include it.
Disabling Remote CAPTCHA Validation on Individual Character Input
Since BotDetect deletes the Captcha code stored on the server after failed validation of a particular Captcha instance (as explained in the BotDetect FAQ), we must avoid validating the user input before the user finished typing in the whole Captcha code. The simplest way to achieve this is to disable onkeyup
validation completely (onkeyup: false
).
Reloading the CAPTCHA Image When Remote Validation Fails
Due to the above (failed validation invalidates the stored code for security purposes), we must also always reload the Captcha image when remote validation fails. Otherwise the user would be trying to correct his input based on an expired image, and couldn't pass Captcha validation at all.
We do this by customizing the jQuery.validate showErrors
callback: beside the regular functionality (this.defaultShowErrors
), we also check that there is a Captcha validation error (element.className.match(/captchaVal/
) and that the error message indicates a remote validation failure (message === this.settings.messages[element.id]. remote
). If that is case, we call the ReloadImage()
function on the client-side Captcha object (accessed through the textbox element as explained above).
Default.aspx.cs
using System; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; public partial class _Default : System.Web.UI.Page { protected void Page_PreRender(object sender, EventArgs e) { // initial page setup if (!IsPostBack) { // set control text ValidateCaptchaButton.Text = "Validate"; CaptchaCorrectLabel.Text = "Successful form submission (server)"; CaptchaIncorrectLabel.Text = "Failed form submission (server)"; // these messages are shown only after validation CaptchaCorrectLabel.Visible = false; CaptchaIncorrectLabel.Visible = false; } // setup client-side input processing ExampleCaptcha.UserInputID = CaptchaCodeTextBox.ClientID; if (IsPostBack) { // validate the Captcha to check we're not dealing with a bot string code = CaptchaCodeTextBox.Text.Trim().ToUpper(); bool isHuman = ExampleCaptcha.Validate(code); CaptchaCodeTextBox.Text = null; // clear previous user input if (isHuman) { CaptchaCorrectLabel.Visible = true; CaptchaIncorrectLabel.Visible = false; } else { CaptchaCorrectLabel.Visible = false; CaptchaIncorrectLabel.Visible = true; } } } }
The input form code is functionally identical to the basic Captcha example, with only the Captcha validation error and success messages changed to make it easier to follow the interaction between client-side and server-side validation.
We left the server-side Captcha validation (implemented in the basic Captcha example) in place, ensuring that any bots or users with JavaScript disabled have their input checked. For users with JavaScript enabled, errors in Captcha code input will be shown on the client without full form postback to the server.
Web.config
<?xml version="1.0"?> <!-- For more information on how to configure your ASP.NET application, please visit http://go.microsoft.com/fwlink/?LinkId=169433 --> <configuration> <configSections> <section name="botDetect" requirePermission="false" type="BotDetect.Configuration.BotDetectConfigurationSection, BotDetect"/> </configSections> <appSettings> <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/> <add key="ValidationSettings:UnobtrusiveValidationMode" value="None"/> </appSettings> <system.web> <httpHandlers> <!-- Register the HttpHandler used for BotDetect Captcha requests --> <add verb="GET" path="BotDetectCaptcha.ashx" type="BotDetect.Web.CaptchaHandler, BotDetect"/> </httpHandlers> <!-- Register a custom SessionIDManager for BotDetect Captcha requests --> <sessionState mode="InProc" cookieless="AutoDetect" timeout="20" sessionIDManagerType="BotDetect.Web.CustomSessionIdManager, BotDetect"/> <!-- Session state is required for BotDetect storage; you can also turn if off globally and only enable for BotDetect-protected pages if you prefer --> <pages controlRenderingCompatibilityVersion="4.0" enableSessionState="true"> <controls> <!-- Register the BotDetect tag prefix for easier use in all pages --> <add assembly="BotDetect" namespace="BotDetect.Web.UI" tagPrefix="BotDetect"/> </controls> </pages> <compilation debug="false" targetFramework="4.5"/> <httpRuntime requestValidationMode="4.5" targetFramework="4.5" encoderType="System.Web.Security.AntiXss.AntiXssEncoder, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/> <machineKey compatibilityMode="Framework45"/> </system.web> <system.webServer> <validation validateIntegratedModeConfiguration="false"/> <handlers> <!-- Register the HttpHandler used for BotDetect Captcha requests (IIS 7.0+) --> <remove name="BotDetectCaptchaHandler"/> <add name="BotDetectCaptchaHandler" preCondition="integratedMode" verb="GET" path="BotDetectCaptcha.ashx" type="BotDetect.Web.CaptchaHandler, BotDetect"/> </handlers> </system.webServer> <botDetect helpLinkEnabled="true" helpLinkMode="image" /> </configuration>
There are several BotDetect-related changes in the web.config
file, including Captcha HttpHandler
registration, ASP.NET Session state configuration, and BotDetect tag prefix registration.
Current BotDetect Versions
-
BotDetect ASP.NET CAPTCHA
2019-07-22v4.4.2 -
BotDetect Java CAPTCHA
2019-07-22v4.0.Beta3.7 -
BotDetect PHP CAPTCHA
2019-07-22v4.2.5