ASP.NET CAPTCHA Request Dynamic Settings C# Code Example

The ASP.NET Captcha request dynamic settings example project shows how to dynamically adjust BotDetect CAPTCHA configuration, potentially on each Http request made by the client.

First Time Here?

Check the BotDetect Developer Crash Course for key integration steps.

Any code setting Captcha properties in the Captcha.InitializedWebCaptcha event handler will be executed not only for each protected form GET or POST request (like Captcha configuration code placed in form source would be), but also for each each GET request loading a Captcha image or sound, or making an Ajax Captcha validation call.

If configured values are dynamic (e.g. randomized from a range), they will be re-calculated for each Captcha challenge generated. For example, Captcha ImageStyle randomized in Captcha.InitializedWebCaptcha event handler code will change on each Captcha reload button click.

This means your code can reliably keep track of visitor interaction with the Captcha challenge and dynamically adjust its settings. Also, while Captcha.InitializedWebCaptcha settings apply to all Captcha instances by default, you can also selectively apply them based on CaptchaId.

To show an example of the possible dynamic Captcha configuration adjustments, this code example increases the difficulty of the Captcha test if the visitor associated with the current ASP.NET Session fails a certain number of Captcha validation attempts, and also sets the Captcha locale to Chinese for requests from a certain IP range.

Download the BotDetect ASP.NET CAPTCHA Generator archive to run this example

Visual Studio 2005-2017 / .NET 2.0 and onwards

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~conf_via-dynamic_config/csharp/ folder; and contains the following files:

Default.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" 
Inherits="_Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
  <title>BotDetect ASP.NET CAPTCHA Options: Request Dynamic Settings Code 
  Example</title>
  <link type="text/css" rel="Stylesheet" href="StyleSheet.css" />
</head>
<body>
  <form runat="server" class="column" id="form1">
    <h1>BotDetect ASP.NET CAPTCHA Options:
      <br />
      Request Dynamic Settings Code Example</h1>

    <fieldset>
      <legend>ASP.NET WebForm CAPTCHA Validation</legend>
      <p class="prompt">
        <label for="CaptchaCodeTextBox">Retype the characters from the picture:</label></p>
      <BotDetect:WebFormsCaptcha runat="server" ID="DynamicCaptcha" 
      UserInputID="CaptchaCodeTextBox" />
      <div class="validationDiv">
        <asp:TextBox ID="CaptchaCodeTextBox" runat="server"></asp:TextBox>
        <asp:Button ID="ValidateCaptchaButton" runat="server" />
        <asp:Label ID="CaptchaCorrectLabel" runat="server" CssClass="correct"></asp:Label>
        <asp:Label ID="CaptchaIncorrectLabel" runat="server" 
        CssClass="incorrect"></asp:Label>
      </div>
    </fieldset>

    <div id="output">
      <asp:Literal runat="server" ID="StatusLiteral" />
    </div>
  </form>
</body> 

Default.aspx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

using BotDetect;
using BotDetect.Web;

public partial class _Default : System.Web.UI.Page
{
  void Page_Init(object sender, EventArgs e)
  {
    Captcha.InitializedWebCaptcha += Captcha_InitializedWebCaptcha;
  }

  void Captcha_InitializedWebCaptcha(object sender, 
  BotDetect.InitializedWebCaptchaEventArgs e)
  {
    Captcha captchaInstance = sender as Captcha;

    // Captcha.InitializedWebCaptcha event handlers are global and apply to all 
    // Captcha instances 
    // in the application; if some settings need to be apply only to a 
    // particular Captcha 
    // instance, this is how settings can be conditionally applied based on 
    // CaptchaId
    if (e.CaptchaId == DynamicCaptcha.CaptchaId)
    {
      captchaInstance.SoundEnabled = false;
    }

    // re-calculated on each image request
    ImageStyle[] imageStyles = { ImageStyle.Graffiti, ImageStyle.SunAndWarmAir, 
    ImageStyle.Overlap };
    captchaInstance.ImageStyle = CaptchaRandomization.GetRandomImageStyle(imageStyles);

    // dynamic Captcha settings depending on failed validation attempts: 
    // increase Captcha 
    // difficulty according to number of previously failed validations
    int count = DynamicCaptchaExample.ValidationCounter.GetFailedValidationsCount();
    if (count < 3)
    {
      captchaInstance.CodeLength = CaptchaRandomization.GetRandomCodeLength(3, 4);
      captchaInstance.CodeStyle = CodeStyle.Numeric;
      captchaInstance.CodeTimeout = 600; // 10 minutes
    }
    else if (count < 10)
    {
      captchaInstance.CodeLength = CaptchaRandomization.GetRandomCodeLength(4, 6)
      ;
      captchaInstance.CodeStyle = CodeStyle.Alpha;
      captchaInstance.CodeTimeout = 180; // 3 minutes
    }
    else
    {
      captchaInstance.CodeLength = CaptchaRandomization.GetRandomCodeLength(6, 9);
      captchaInstance.CodeStyle = CodeStyle.Alphanumeric;
      captchaInstance.CodeTimeout = 60; // 1 minute
    }

    // set Captcha locale to Chinese for requests from a certain IP range
    string ipRange = "223.254.";
    bool requestFromRangeDetected = false;

    // have to use HttpContext.Current.Request and not Page.Request because Page
    // properties won't be set for Captcha image and sound requests serverd 
    // directly
    // by the BotDetect Captcha HttpHandler
    if (null != HttpContext.Current.Request &&
            !String.IsNullOrEmpty(HttpContext.Current.Request.UserHostAddress) 
            && HttpContext.Current.Request.UserHostAddress.StartsWith(ipRange))
    {
      requestFromRangeDetected = true;
    }

    if (requestFromRangeDetected)
    {
      captchaInstance.CodeStyle = CodeStyle.Alpha;
      captchaInstance.Locale = "cmn";
    }
  }


  protected void Page_PreRender(object sender, EventArgs e)
  {
    // initial page setup
    if (!IsPostBack)
    {
      // set control text
      ValidateCaptchaButton.Text = "Validate";
      CaptchaCorrectLabel.Text = "Correct!";
      CaptchaIncorrectLabel.Text = "Incorrect!";

      // these messages are shown only after validation
      CaptchaCorrectLabel.Visible = false;
      CaptchaIncorrectLabel.Visible = false;
    }

    if (IsPostBack)
    {
      // validate the Captcha to check we're not dealing with a bot
      bool isHuman = DynamicCaptcha.Validate();
      if (isHuman)
      {
        CaptchaCorrectLabel.Visible = true;
        CaptchaIncorrectLabel.Visible = false;
        DynamicCaptchaExample.ValidationCounter.ResetFailedValidationsCount();
      }
      else
      {
        CaptchaCorrectLabel.Visible = false;
        CaptchaIncorrectLabel.Visible = true;
        DynamicCaptchaExample.ValidationCounter.IncrementFailedValidationsCount();
      }
    }

    // update status display
    int count = DynamicCaptchaExample.ValidationCounter.GetFailedValidationsCount();
    StatusLiteral.Text = String.Format("<p>Failed Captcha validations: {0}</p>", count);
    if (count < 3)
    {
      StatusLiteral.Text += "<p>Dynamic Captcha difficulty: Easy</p>";
    }
    else if (count < 10)
    {
      StatusLiteral.Text += "<p>Dynamic Captcha difficulty: Moderate</p>";
    }
    else
    {
      StatusLiteral.Text += "<p>Dynamic Captcha difficulty: Hard</p>";
    }
  }
}

App_Code\Counter.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace DynamicCaptchaExample
{
  /// <summary>
  /// Summary description for Counter
  /// </summary>
  public static class ValidationCounter
  {
    const string FailedValidationsCountKey = "FailedValidationsCountKey";

    public static int GetFailedValidationsCount()
    {
      int count = 0;
      object saved = HttpContext.Current.Session[FailedValidationsCountKey];
      if (null != saved)
      {
        try
        {
          count = (int)saved;
        }
        catch (InvalidCastException)
        {
          // ignore cast errors
          count = 0;
        }
      }
      return count;
    }

    public static void IncrementFailedValidationsCount()
    {
      int count = GetFailedValidationsCount();
      count++;
      HttpContext.Current.Session[FailedValidationsCountKey] = count;
    }

    public static void ResetFailedValidationsCount()
    {
      HttpContext.Current.Session.Remove(FailedValidationsCountKey);
    }
  }

}

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.5" 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>