ASP.NET Membership CAPTCHA VB.NET Code Sample (BotDetect v3.0; deprecated)
The ASP.NET Membership Captcha sample 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 sample 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.
- C#
- VB.NET
- Visual Studio 2013 / .NET 4.5.1
- Visual Studio 2012 / .NET 4.5
- Visual Studio 2010 / .NET 4.0
- Visual Studio 2008 / .NET 3.5
- Visual Studio 2005 / .NET 2.0
Visual Studio 2013 / .NET 4.5.1
Since ASP.NET Membership has been deprecated in Visual Studio 2013 and replaced with ASP.NET 4.5 WebForms Application Template Captcha code sample.
Visual Studio 2012 / .NET 4.5
By default, the .NET 4.5 VB.NET version of the ASP.NET Membership Captcha sample project is installed at:
C:\Program Files\Lanapsoft\BotDetect 3 CAPTCHA Component\Asp.Net\v4.5\WebApp\AspNetMembershipCaptchaSample\VBNet
You can also run it from the BotDetect Start Menu:
Programs > Lanapsoft > BotDetect 3 CAPTCHA Component > ASP.NET > DotNET 4.5 Web Applications > Run
The Visual Studio 2012 / .NET 4.5 source has no essential differences from the Visual Studio 2010 / .NET 4.0 source.
Visual Studio 2010 / .NET 4.0
By default, the .NET 4.0 VB.NET version of the ASP.NET Membership Captcha sample project is installed at:
C:\Program Files\Lanapsoft\BotDetect 3 CAPTCHA Component\Asp.Net\v4.0\WebApp\AspNetMembershipCaptchaSample\VBNet
You can also run it from the BotDetect Start Menu:
Programs > Lanapsoft > BotDetect 3 CAPTCHA Component > ASP.NET > DotNET 4.0 Web Applications > Run
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 Sample</title> <link type="text/css" rel="Stylesheet" href="StyleSheet.css" /> </head> <body> <form id="form1" runat="server"> <h1>BotDetect CAPTCHA ASP.NET Membership Sample</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 Sample</h1> <fieldset> <legend>CAPTCHA Validation in a <code>Login</code> control</legend> <asp:Login ID="SampleLogin" runat="server" OnAuthenticate="SampleLogin_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="SampleLogin">*</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="SampleLogin">*</asp: RequiredFieldValidator> </td> </tr> <tr> <td colspan="2"> <BotDetect:Captcha ID="LoginCaptcha" 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="SampleLogin">*</asp: RequiredFieldValidator> </td> </tr> <tr> <td align="center" colspan="2" style="color:Red;"> <asp:Literal ID="FailureText" runat="server" EnableViewState="False"></asp:Literal> </td> </tr> <tr> <td align="right" colspan="2"> <asp:Button ID="LoginButton" runat="server" CommandName="Login" Text="Log In" ValidationGroup="SampleLogin" /> </td> </tr> </table> </td> </tr> </table> </LayoutTemplate> </asp:Login> <p><a href="Register.aspx">Register</a></p> </fieldset> </form> </body> </html>
The <BotDetect:Captcha>
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 SampleLogin_Authenticate(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.AuthenticateEventArgs) Dim CaptchaCodeTextBox As TextBox = TryCast(SampleLogin. FindControl("CaptchaCodeTextBox"), TextBox) Dim LoginCaptcha As Captcha = TryCast(SampleLogin.FindControl( "LoginCaptcha"), Captcha) '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().ToUpper() isHuman = LoginCaptcha.Validate(code) CaptchaCodeTextBox.Text = "" ' clear previous user input If Not isHuman Then SampleLogin.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(SampleLogin.UserName, SampleLogin.Password) Then SampleLogin.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 Captcha = TryCast(SampleLogin.FindControl( "LoginCaptcha"), Captcha) Dim CaptchaRow As HtmlControl = TryCast(SampleLogin. FindControl("CaptchaRow"), HtmlControl) CaptchaRow.Visible = False LoginCaptcha.Visible = False End Sub Protected Sub ShowCaptcha() Dim LoginCaptcha As Captcha = TryCast(SampleLogin.FindControl( "LoginCaptcha"), Captcha) Dim CaptchaRow As HtmlControl = TryCast(SampleLogin. 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 SampleLogin_Authenticate
event. To get the Captcha code textbox and Captcha
control instances, we have to use SampleLogin.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 Sample</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 colspan="2"> <BotDetect:Captcha ID="RegisterCaptcha" 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;"> <asp:Literal ID="InvalidCaptchaInput" runat="server" EnableViewState="False" Visible="False" Text="Retype the characters from the image carefully."></asp: Literal> </td> <td align="center" colspan="2" style="color:Red;"> <asp:Literal ID="ErrorMessage" runat="server" EnableViewState="False"></asp:Literal> </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:Captcha>
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 Captcha = TryCast(RegisterUser. CreateUserStep.ContentTemplateContainer.FindControl( "RegisterCaptcha"), Captcha) 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().ToUpper() isHuman = RegisterCaptcha.Validate(code) CaptchaCodeTextBox.Text = "" ' clear previous user input If 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 xmlns="http://schemas.microsoft.com/. NetConfiguration/v2.0"> <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"> <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="true" targetFramework="4.0"> <assemblies> <add assembly="System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/> </assemblies> </compilation> <trace enabled="false" localOnly="true"/> <httpCookies httpOnlyCookies="true"/> <trust level="Medium" originUrl=""/> <customErrors mode="RemoteOnly"></customErrors> <!-- 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> </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 sample 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 sample installation).
Visual Studio 2008 / .NET 3.5
By default, the .NET 3.5 VB.NET version of the ASP.NET Membership Captcha sample project is installed at:
C:\Program Files\Lanapsoft\BotDetect 3 CAPTCHA Component\Asp.Net\v3.5\WebApp\AspNetMembershipCaptchaSample\VBNet
You can also run it from the BotDetect Start Menu:
Programs > Lanapsoft > BotDetect 3 CAPTCHA Component > ASP.NET > DotNET 3.5 Web Applications > Run
The Visual Studio 2008 / .NET 3.5 source has no essential differences from the Visual Studio 2010 / .NET 4.0 source.
Visual Studio 2005 / .NET 2.0
By default, the .NET 2.0 VB.NET version of the ASP.NET Membership Captcha sample project is installed at:
C:\Program Files\Lanapsoft\BotDetect 3 CAPTCHA Component\Asp.Net\v2.0\WebApp\AspNetMembershipCaptchaSample\VBNet
You can also run it from the BotDetect Start Menu:
Programs > Lanapsoft > BotDetect 3 CAPTCHA Component > ASP.NET > DotNET 2.0 Web Applications > Run
The Visual Studio 2005 / .NET 2.0 source has no essential differences from the Visual Studio 2010 / .NET 4.0 source.
Please Note
The information on this page is out of date and applies to a deprecated version of BotDetect™ CAPTCHA (v3.0).
An up-to-date equivalent page for the latest BotDetect Captcha release (v4) is BotDetect v4 Captcha documentation index.
General information about the major improvements in the current BotDetect release can be found at the What's New in BotDetect v4.0 page.
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