ASP.NET Membership CAPTCHA VB.NET Code Example

The ASP.NET Membership Captcha example project shows how to integrate BotDetect CAPTCHA validation with standard ASP.NET Membership functionality used in ASP.NET Login and CreateUserWizard controls.

First Time Here?

Check the BotDetect ASP.NET WebForms Captcha Quickstart for key integration steps.

To prevent bots from trying to guess the login info by brute force submission of a large number of common values, the visitor first has to prove they are human (by solving the Captcha), and only then is their username and password submission checked against the authentication data store.

Also, if they enter an invalid username + password combination three times, they have to solve the Captcha again. This prevents attempts in which the attacker would first solve the Captcha themselves, and then let a bot brute-force the authentication info.

To keep the example code simple, the example doesn't access a data store to authenticate the user, but accepts all logins with usernames and passwords at least 5 characters long as valid.

And to prevent bots from registering user accounts, the Register page Captcha has to be solved before user details are recorded.

Download the BotDetect ASP.NET CAPTCHA Generator archive to run this example
  • C#
  • VB.NET

Visual Studio 2017, 2015, 2013 / .NET 4.5.1 and onwards

Since ASP.NET Membership has been deprecated in Visual Studio 2013 and replaced with ASP.NET Identity, there is no Visual Studio 2013 version of the ASP.NET Membership Captcha code example. You can see how to integrate BotDetect Captcha validation into ASP.NET WebForms applications using ASP.NET Identity user management in the ASP.NET WebForms Application Template Captcha code example.

Visual Studio 2012, 2010, 2008, 2005 / .NET 4.5, .NET 4.0, .NET 3.5, .NET 2.0

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-web.security.membership/vbnet/ folder; and contains the following files:

Default.aspx

<%@ Page Language="VB" AutoEventWireup="true" CodeFile="Default.aspx.vb" 
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 CAPTCHA ASP.NET Membership Example</title> 
  <link type="text/css" rel="Stylesheet" href="StyleSheet.css" /> 
</head> 
<body> 
  <form id="form1" runat="server"> 
    <h1>BotDetect CAPTCHA ASP.NET Membership Example</h1> 
    <fieldset> 
      <legend>Page protected with ASP.NET authentication</legend> 
      <p>This page can only be seen after successful Captcha validation and 
      username/password authentication.</p> 
      <asp:LinkButton Text="Sign out" runat="server" ID="SignOutButton" 
      OnClick="SignOutButton_Click"></asp:LinkButton> 
    </fieldset> 
  </form> 
</body> 
</html> 

This page has no special code, but is meant to only be seen by authenticated users. It will be displayed to users who pass both the Captcha validation and the username / password authentication.

Default.aspx.vb

 
Partial Class _Default 
  Inherits System.Web.UI.Page 

  Protected Sub SignOutButton_Click(ByVal sender As Object, 
  ByVal e As System.EventArgs) Handles SignOutButton.Click 
    FormsAuthentication.SignOut() 
    Response.Redirect("Default.aspx?ref=signout", True) 
  End Sub 
End Class 

Login.aspx

<%@ Page Language="VB" AutoEventWireup="true" CodeFile="Login.aspx.vb" 
Inherits="Login" %> 

<!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 runat="server"> 
  <title>Login</title> 
  <link type="text/css" rel="Stylesheet" href="StyleSheet.css" /> 
</head> 
<body> 
  <form id="form1" runat="server"> 
    <h1>BotDetect CAPTCHA ASP.NET Membership Example</h1> 
    <fieldset> 
      <legend>CAPTCHA Validation in a <code>Login</code> control</legend> 
      <asp:Login ID="ExampleLogin" runat="server" 
      OnAuthenticate="ExampleLogin_Authenticate"> 
        <LayoutTemplate> 
          <table border="0" cellpadding="1" cellspacing="0" 
            style="border-collapse: collapse;"> 
            <tr> 
              <td> 
                <table border="0" cellpadding="0"> 
                  <tr> 
                    <td align="center" colspan="2">Log In</td> 
                  </tr> 
                  <tr> 
                    <td align="right" width="185"> 
                      <asp:Label ID="UserNameLabel" runat="server" 
                      AssociatedControlID="UserName">Username:</asp:Label> 
                    </td> 
                    <td> 
                      <asp:TextBox ID="UserName" runat="server"></asp:TextBox> 
                      <asp:RequiredFieldValidator ID="UserNameRequired" 
                      runat="server" ControlToValidate="UserName" 
                        ErrorMessage="User Name is required." 
                        ToolTip="User Name is required." 
                        ValidationGroup="ExampleLogin">*</asp:RequiredFieldValidator> 
                    </td> 
                  </tr> 
                  <tr> 
                    <td align="right"> 
                      <asp:Label ID="PasswordLabel" runat="server" 
                      AssociatedControlID="Password">Password:</asp:Label> 
                    </td> 
                    <td> 
                      <asp:TextBox ID="Password" runat="server" 
                      TextMode="Password"></asp:TextBox> 
                      <asp:RequiredFieldValidator ID="PasswordRequired" 
                      runat="server" ControlToValidate="Password" 
                        ErrorMessage="Password is required." 
                        ToolTip="Password is required." 
                        ValidationGroup="ExampleLogin">*</asp:RequiredFieldValidator> 
                    </td> 
                  </tr> 
                  <tr> 
                    <td></td> 
                    <td> 
                      <BotDetect:WebFormsCaptcha ID="LoginCaptcha" 
                      UserInputID="CaptchaCodeTextBox" 
                      ImageSize="150, 50" CodeLength="3" runat="server" /> 
                    </td> 
                  </tr> 
                  <tr runat="server" id="CaptchaRow"> 
                    <td align="right"> 
                      <asp:Label ID="CaptchaLabel" runat="server" 
                      AssociatedControlID="CaptchaCodeTextBox">Code:</asp:Label> 
                    </td> 
                    <td> 
                      <asp:TextBox ID="CaptchaCodeTextBox" runat="server"></asp:TextBox> 
                      <asp:RequiredFieldValidator ID="CaptchaRequiredValidator" 
                      runat="server" 
                        ControlToValidate="CaptchaCodeTextBox" 
                        ErrorMessage="CAPTCHA code is required." 
                        ToolTip="CAPTCHA code is required." 
                        ValidationGroup="ExampleLogin">*</asp:RequiredFieldValidator> 
                    </td> 
                  </tr> 
                  <tr> 
                    <td align="center" colspan="2" style="color: Red;"> 
                      <span> 
                        <asp:Literal ID="FailureText" runat="server" 
                        EnableViewState="False"></asp:Literal></span> 
                    </td> 
                  </tr> 
                  <tr> 
                    <td align="right" colspan="2"> 
                      <asp:Button ID="LoginButton" runat="server" 
                      CommandName="Login" Text="Log In" 
                        ValidationGroup="ExampleLogin" /> 
                    </td> 
                  </tr> 
                </table> 
              </td> 
            </tr> 
          </table> 
        </LayoutTemplate> 
      </asp:Login> 
      <p><a href="Register.aspx">Register</a></p> 
    </fieldset> 
  </form> 
</body> 
</html> 

The <BotDetect:WebFormsCaptcha> control is added to the Login form below the username and password fields. An <asp:RequiredFieldValidator> is added and connected with the Captcha code textbox, to warn users not to submit the form without entering the Captcha code.

Login.aspx.vb

Imports BotDetect.Web.UI 

Partial Class Login 
  Inherits System.Web.UI.Page 

  Protected Sub ExampleLogin_Authenticate(ByVal sender As Object, 
  ByVal e As System.Web.UI.WebControls.AuthenticateEventArgs) 
    Dim CaptchaCodeTextBox As TextBox = TryCast(ExampleLogin.FindControl( 
    "CaptchaCodeTextBox"), TextBox) 
    Dim LoginCaptcha As WebFormsCaptcha = TryCast(ExampleLogin.FindControl( 
    "LoginCaptcha"), WebFormsCaptcha) 

    'first, validate the Captcha to check we're not dealing with a bot 
    If (Not IsHuman) Then 
      Dim code As String, isHuman As Boolean 
      code = CaptchaCodeTextBox.Text.Trim() 
      isHuman = LoginCaptcha.Validate(code) 
      CaptchaCodeTextBox.Text = "" ' clear previous user input 

      If Not isHuman Then 
        ExampleLogin.FailureText = "Retype the characters from the image carefully." 
        e.Authenticated = False 
        Return 
      End If 

      HideCaptcha() ' hide the CAPTCHA once it's solved 

      'only when we're sure the visitor is human, we try to authenticate them 
      If Not Membership.ValidateUser(ExampleLogin.UserName, ExampleLogin.Password) 
      Then 
        ExampleLogin.FailureText = "Invalid login info." 
        e.Authenticated = False 

        FailedAuthAttempts = FailedAuthAttempts + 1 
        If (ResetFailedAuthAttempts < FailedAuthAttempts) Then 
          ' show the CAPTCHA again if the user enters invalid authentication 
          ' info three times in a row 
          ShowCaptcha() 
        End If 

        Return 
      End If 
    End If 

    e.Authenticated = True 
  End Sub 

  Protected Sub HideCaptcha() 
    Dim LoginCaptcha As WebFormsCaptcha = TryCast(ExampleLogin.FindControl( 
    "LoginCaptcha"), WebFormsCaptcha) 
    Dim CaptchaRow As HtmlControl = TryCast(ExampleLogin.FindControl("CaptchaRow") 
    , HtmlControl) 

    CaptchaRow.Visible = False 
    LoginCaptcha.Visible = False 
  End Sub 

  Protected Sub ShowCaptcha() 
    Dim LoginCaptcha As WebFormsCaptcha = TryCast(ExampleLogin.FindControl( 
    "LoginCaptcha"), WebFormsCaptcha) 
    Dim CaptchaRow As HtmlControl = TryCast(ExampleLogin.FindControl("CaptchaRow") 
    , HtmlControl) 

    IsHuman = False 
    FailedAuthAttempts = 0 
    CaptchaRow.Visible = True 
    LoginCaptcha.Visible = True 
  End Sub 

  ' flag showing the user successfully passed the CAPTCHA test 
  Protected Property IsHuman() As Boolean 
    Get 
      Dim result As Boolean = False 
      Try 
        If (Nothing <> Session("IsHuman")) Then 
          result = CBool(Session("IsHuman")) 
        End If 
      Catch e As InvalidCastException 
      End Try 

      Return result 
    End Get 

    Set(ByVal value As Boolean) 
      Session("IsHuman") = value 
    End Set 
  End Property 

  Protected Const ResetFailedAuthAttempts As Integer = 3 

  ' failed authentication attempt counter 
  Protected Property FailedAuthAttempts() As Integer 
    Get 
      Dim count As Integer = 0 
      Try 
        If (Nothing <> Session("FailedAuthAttempts")) Then 
          count = CInt(Session("FailedAuthAttempts")) 
        End If 
      Catch e As InvalidCastException 
      End Try 

      Return count 
    End Get 

    Set(ByVal value As Integer) 
      Session("FailedAuthAttempts") = value 
    End Set 
  End Property 

End Class 

Since we want to process login attempts as they are submitted, we handle the ExampleLogin_Authenticate event. To get the Captcha code textbox and Captcha control instances, we have to use ExampleLogin.FindControl() calls (as they can't be accessed directly by ID in this event handler).

Since we want to remember when the user correctly solved the Captcha (so they don't have to immediately solve another one if the fail the first authentication attempt), we keep it in the IsHuman property wrapping Session persistence. And since we want to reset the Captcha status after a number of failed authentications (3 by default, as defined in the ResetFailedAuthAttempts constant), we keep the related counter in the FailedAuthAttempts property.

This allows us to implement the proper Login form workflow:

  • The user must first solve the Captcha to prove they are human. This keeps the bots away from the Membership provider, both conserving its resources and improving its security (since usernames and passwords will not be forwarded to the underlying data store if the Captcha is not solved first).
  • When the user has proven they are human, they get 3 authentication attempts without new Captcha tests, which allows them to remember the right combination in most cases.
  • If the user fails three authentication requests, they are shown a new Captcha which they must solve before continuing. This throttles authentication access, ensuring username + password combinations cannot be brute-forced, while real human users get theoretically unlimited authentication attempts (as long as they don't mind solving further Captchas).

Register.aspx

<%@ Page Language="VB" AutoEventWireup="true" CodeFile="Register.aspx.vb" 
Inherits="Register" %> 

<!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 runat="server"> 
  <title>Register</title> 
  <link type="text/css" rel="Stylesheet" href="StyleSheet.css" /> 
</head> 
<body> 
  <form id="form1" runat="server"> 
    <h1>BotDetect CAPTCHA ASP.NET Membership Example</h1> 
    <fieldset> 
      <legend>CAPTCHA Validation in a <code>CreateUserWizard</code> control</legend> 
      <asp:CreateUserWizard ID="RegisterUser" runat="server" 
        OnNextButtonClick="RegisterUser_NextButtonClick" ActiveStepIndex="0"> 
        <WizardSteps> 
          <asp:CreateUserWizardStep runat="server"> 
            <ContentTemplate> 
              <table border="0"> 
                <tr> 
                  <td align="center" colspan="2">Sign Up for Your New Account</td> 
                </tr> 
                <tr> 
                  <td align="right" width="185"> 
                    <asp:Label ID="UserNameLabel" runat="server" 
                    AssociatedControlID="UserName">Username:</asp:Label> 
                  </td> 
                  <td> 
                    <asp:TextBox ID="UserName" runat="server"></asp:TextBox> 
                    <asp:RequiredFieldValidator ID="UserNameRequired" 
                    runat="server" ControlToValidate="UserName" 
                      ErrorMessage="User Name is  required." 
                      ToolTip="User Name is required." 
                      ValidationGroup="RegisterUser">*</asp:RequiredFieldValidator> 
                  </td> 
                </tr> 
                <tr> 
                  <td align="right"> 
                    <asp:Label ID="PasswordLabel" runat="server" 
                    AssociatedControlID="Password">Password:</asp:Label> 
                  </td> 
                  <td> 
                    <asp:TextBox ID="Password" runat="server" 
                    TextMode="Password"></asp:TextBox> 
                    <asp:RequiredFieldValidator ID="PasswordRequired" 
                    runat="server" ControlToValidate="Password" 
                      ErrorMessage="Password is required." 
                      ToolTip="Password is required." 
                      ValidationGroup="RegisterUser">*</asp:RequiredFieldValidator> 
                  </td> 
                </tr> 
                <tr> 
                  <td align="right"> 
                    <asp:Label ID="ConfirmPasswordLabel" runat="server" 
                      AssociatedControlID="ConfirmPassword">Confirm Password:</asp:Label> 
                  </td> 
                  <td> 
                    <asp:TextBox ID="ConfirmPassword" runat="server" 
                    TextMode="Password"></asp:TextBox> 
                    <asp:RequiredFieldValidator ID="ConfirmPasswordRequired" 
                    runat="server" 
                      ControlToValidate="ConfirmPassword" 
                      ErrorMessage="Confirm Password is required." 
                      ToolTip="Confirm Password is required." 
                      ValidationGroup="RegisterUser">*</asp:RequiredFieldValidator> 
                  </td> 
                </tr> 
                <tr> 
                  <td align="center" colspan="2"> 
                    <asp:CompareValidator ID="PasswordCompare" runat="server" 
                      ControlToCompare="Password" 
                      ControlToValidate="ConfirmPassword" 
                      Display="Dynamic" 
                      ErrorMessage="The Password and Confirmation Password must match." 
                      ValidationGroup="RegisterUser"></asp:CompareValidator> 
                  </td> 
                </tr> 
                <tr> 
                  <td align="right"> 
                    <asp:Label ID="EmailLabel" runat="server" 
                    AssociatedControlID="Email">E-mail:</asp:Label> 
                  </td> 
                  <td> 
                    <asp:TextBox ID="Email" runat="server"></asp:TextBox> 
                    <asp:RequiredFieldValidator ID="EmailRequired" 
                    runat="server" 
                      ControlToValidate="Email" ErrorMessage="E-mail is required. " 
                      ToolTip="E-mail is required." 
                      ValidationGroup="RegisterUser">*</asp:RequiredFieldValidator> 
                  </td> 
                </tr> 
                <tr> 
                  <td></td> 
                  <td> 
                    <BotDetect:WebFormsCaptcha ID="RegisterCaptcha" 
                    UserInputID="CaptchaCodeTextBox" 
                    ImageSize="150, 50" CodeLength="3" runat="server" /> 
                  </td> 
                </tr> 
                <tr> 
                  <td align="right"> 
                    <asp:Label ID="CaptchaLabel" runat="server" 
                    AssociatedControlID="CaptchaCodeTextBox">Code:</asp:Label> 
                  </td> 
                  <td> 
                    <asp:TextBox ID="CaptchaCodeTextBox" runat="server"></asp:TextBox> 
                    <asp:RequiredFieldValidator ID="CaptchaRequiredValidator" 
                    runat="server" 
                      ControlToValidate="CaptchaCodeTextBox" 
                      ErrorMessage="CAPTCHA code is required." 
                      ToolTip="CAPTCHA code is required." 
                      ValidationGroup="RegisterUser">*</asp:RequiredFieldValidator> 
                  </td> 
                </tr> 
                <tr> 
                  <td align="center" colspan="2" style="color: Red;"> 
                    <span> 
                      <asp:Literal ID="InvalidCaptchaInput" runat="server" 
                      EnableViewState="False" Visible="False" Text="Retype the  
                      characters from the image carefully."></asp:Literal></span> 
                  </td> 
                  <td align="center" colspan="2" style="color: Red;"> 
                    <span> 
                      <asp:Literal ID="ErrorMessage" runat="server" 
                      EnableViewState="False"></asp:Literal></span> 
                  </td> 
                </tr> 
              </table> 
            </ContentTemplate> 
          </asp:CreateUserWizardStep> 
          <asp:CompleteWizardStep runat="server"> 
            <ContentTemplate> 
              <table border="0"> 
                <tr> 
                  <td align="center" colspan="2">Complete</td> 
                </tr> 
                <tr> 
                  <td>Your account has been successfully created.</td> 
                </tr> 
                <tr> 
                  <td align="right" colspan="2"> 
                    <p><a href="Default.aspx">Continue</a></p> 
                  </td> 
                </tr> 
              </table> 
            </ContentTemplate> 
          </asp:CompleteWizardStep> 
        </WizardSteps> 
      </asp:CreateUserWizard> 
      <p><a href="Login.aspx">Login</a></p> 
    </fieldset> 
  </form> 
</body> 
</html> 

To protect the user registration field from automated submissions, a <BotDetect:WebFormsCaptcha> control is added to the CreateUserWizard template.

Register.aspx.vb

Imports BotDetect.Web.UI 

Partial Class Register 
  Inherits System.Web.UI.Page 

  Protected Sub RegisterUser_NextButtonClick(ByVal sender As Object, 
  ByVal e As System.Web.UI.WebControls.WizardNavigationEventArgs) 

    If e.CurrentStepIndex = 0 Then 'CreateUserStep 

      ' get control references 
      Dim RegisterCaptcha As WebFormsCaptcha = TryCast(RegisterUser. 
      CreateUserStep.ContentTemplateContainer.FindControl("RegisterCaptcha"), 
      WebFormsCaptcha) 
      Dim CaptchaCodeTextBox As TextBox = 
      TryCast(RegisterUser.CreateUserStep.ContentTemplateContainer.FindControl("CaptchaCodeTextBox"), TextBox) 
      Dim CaptchaIncorrect As Literal = 
      TryCast(RegisterUser.CreateUserStep.ContentTemplateContainer.FindControl("InvalidCaptchaInput"), Literal) 

      ' validate the Captcha to check we're not dealing with a bot 
      Dim code As String, isHuman As Boolean 
      code = CaptchaCodeTextBox.Text.Trim() 
      isHuman = RegisterCaptcha.Validate(code) 
      CaptchaCodeTextBox.Text = "" ' clear previous user input 

      If Not isHuman Then 
        CaptchaIncorrect.Visible = True 
        e.Cancel = True 
      Else 
        CaptchaIncorrect.Visible = False 
      End If 
    End If 

  End Sub 
End Class 

To process user creation attempts, we handle the RegisterUser_NextButtonClick event and make sure the code is executed during the first wizard step where Captcha protection was added (If e.CurrentStepIndex = 0).

We use ContentTemplateContainer.FindControl() calls to get all necessary control references, and simply cancel account creation unless Captcha validation succeeds.

Web.config

<?xml version="1.0"?> 
<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 enableSessionState="true" controlRenderingCompatibilityVersion="4.5"> 
      <controls> 
        <!-- Register the BotDetect tag prefix for easier use in all pages --> 
        <add assembly="BotDetect" namespace="BotDetect.Web.UI" 
        tagPrefix="BotDetect"/> 
      </controls> 
      <namespaces> 
        <add namespace="BotDetect"/> 
        <add namespace="BotDetect.Web"/> 
        <add namespace="BotDetect.Web.UI"/> 
      </namespaces> 
    </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" /> 
    <trace enabled="false" localOnly="true"/> 
    <httpCookies httpOnlyCookies="true"/> 
    <trust level="Full" originUrl=""/> 
    <customErrors mode="RemoteOnly"/> 
    <!-- Forms authentication --> 
    <authentication mode="Forms"> 
      <forms loginUrl="Login.aspx"/> 
    </authentication> 
    <!-- Deny access to all pages to unauthorized users --> 
    <authorization> 
      <deny users="?"/> 
      <allow users="*"/> 
    </authorization> 
    <!-- Membership provider --> 
    <membership defaultProvider="MockMembershipProvider"> 
      <providers> 
        <add name="MockMembershipProvider" applicationName="/" 
        type="Sample.MockMembershipProvider"/> 
      </providers> 
    </membership> 
  </system.web> 
  <!-- Allow unauthorized access to the Login page --> 
  <location path="Login.aspx"> 
    <system.web> 
      <authorization> 
        <allow users="*"/> 
      </authorization> 
    </system.web> 
  </location> 
  <!-- Allow unauthorized access to the Register page --> 
  <location path="Register.aspx"> 
    <system.web> 
      <authorization> 
        <allow users="*"/> 
      </authorization> 
    </system.web> 
  </location> 
  <!-- Allow unauthorized access to BotDetect Captcha images and sounds --> 
  <location path="BotDetectCaptcha.ashx"> 
    <system.web> 
      <authorization> 
        <allow users="*"/> 
      </authorization> 
    </system.web> 
  </location> 
  <!-- Allow unauthorized access to the Stylesheet --> 
  <location path="StyleSheet.css"> 
    <system.web> 
      <authorization> 
        <allow users="*"/> 
      </authorization> 
    </system.web> 
  </location> 
  <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> 

Besides the usual BotDetect-related web.config changes (Captcha HttpHandler registration, ASP.NET Session state configuration, and BotDetect tag prefix registration), the authentication and authorization elements define access restrictions, while the location elements specify exceptions to those restrictions. In this case, unauthorized users can only access the Login and Register pages and BotDetect Captcha paths.

To keep the example as simple as possible, we use the MockMembershipProvider instead of a real MembershipProvider. It doesn't access any data store to verify the user's credentials, but considers all authorization requests with usernames and passwords longer than 5 characters as valid. Since it doesn't affect Captcha protection and validation logic, MockMembershipProvider is not shown (but is included in the example installation).