BotDetect CAPTCHA Validation ASP Code Sample
The CAPTCHA validation sample contains the basic code required to show a CAPTCHA on a ASP page and validate the user input, allowing users access to a protected page only if they successfully solve the CAPTCHA.
It can be used as a starting point when you are first learning how to use BotDetect, and is equivalent to the result you will get if you are following the How To add BotDetect CAPTCHA protection to ASP forms instructions.
- Sample Project Location
- BotDetectFormDemo.asp
- ProcessForm.asp
- LanapBotDetectHandler.asp
- BotDetectScript.js
- FormStyle.css
Sample Project Location
By default, this sample project is installed at
c:\Program Files\Lanapsoft\BotDetect\ASP\v2.0\Samples\CaptchaValidation\.
You can also run it from the Start Menu:
Programs > Lanapsoft > BotDetect > ASP > v2.0 > Samples > CAPTCHA Validation Sample.
BotDetectFormDemo.asp
Full Source Code Listing
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>BotDetect CAPTCHA ASP Form Demo - Input Page</title>
<link type='text/css' rel='Stylesheet' href='FormStyle.css' />
<script type="text/javascript" src="BotDetectScript.js"></script>
</head>
<body>
<form name="SampleForm" id="SampleForm" method="post"
action="ProcessForm.asp">
<fieldset id="SampleFields">
<legend>Sample input form</legend>
<div class="input">
<label for="FirstName">First Name:</label>
<input name="FirstName" id="FirstName" type="text"
class="textbox" value="<%=Request("FirstName") %>" />
</div>
<div class="input">
<label for="LastName">Last Name:</label>
<input name="LastName" type="text" id="LastName"
class="textbox" value="<%= Request("LastName") %>" />
</div>
</fieldset>
<fieldset id="CaptchaValidation">
<legend>CAPTCHA Validation</legend>
<div id="PromptDiv">Retype the code from the picture</div>
<div id="CaptchaDiv">
<div id="CaptchaImage">
<img id="SampleForm_CaptchaImage" src="
LanapBotDetectHandler.asp?Command=CreateImage&
TextStyle=4&ImageWidth=238&imageHeight=50&
CodeLength=5&CodeType=0" alt='CAPTCHA Code Image' />
</div>
<div id="CaptchaIcons">
<a href='LanapBotDetectHandler.asp?Command=CreateSound'
onclick='LBD_LoadSound("SampleForm_SoundPlaceholder",
"LanapBotDetectHandler.asp?Command=CreateSound");
return false;' title="Speak the code"><img src="
speaker.gif" alt="Speak the code" /></a>
<a href='#' onclick='LBD_ReloadImage(
"SampleForm_CaptchaImage"); return false;' title="
Change the code"><img src="reload.gif" alt="Change the
code" /></a>
<div id='SampleForm_SoundPlaceholder'
class="placeholder"> </div>
</div>
</div>
<div class="input">
<label for="CaptchaCode">Code:</label>
<input name="CaptchaCode" id="CaptchaCode" type="text"
class="textbox" onkeyup="this.value =
this.value.toLowerCase();" />
</div>
<%
If (Request("WrongCode")<>"") Then
Response.Write("<div><span id='CodeIncorrectLabel'>
Incorrect code</span></div>")
End If
%>
</fieldset>
<div id="ActionDiv">
<input type="submit" name="ProcessForm" value="Process Form"
id="ProcessForm" />
</div>
<div id="Note">
<span>NOTE: the Trial version will use "LANAP" instead of a
random code in 50% of CAPTCHA images.</span>
</div>
</form>
</body>
</html>
Explanation
Several Html elements are involved in adding BotDetect CAPTCHA protection to the ASP form:
- In the <head> section of the document, we include a stylesheet which, among other things, defines the CAPTCHA elements layout (FormStyle.css), and the JavaScript file containing BotDetect helper functions for playing the audio CAPTCHA and reloading the CAPTCHA image (BotDetectScript.js).
- We include the CAPTCHA image in the <body> of the page where we want it to be displayed, simply specifying LanapBotDetectHandler.asp?Command=CreateImage as the image source, and passing any optional image settings in the querystring.
- Next to the CAPTCHA image we show a couple of icons allowing the users to use the sound CAPTCHA if they perfer it to the image one, or change the CAPTCHA image in case they find it hard to read. These icons link to JavaScript calls of functions from the included client-script library. The JavaScript sound playback also requires and empty placeholder <div>, where the sound <object>/<embed> element will be created on the speaker icon click.
- Also, note that the sound CAPTCHA link executes the JavaScript call and aborts following the link (with return false;) – if users have JavaScript disabled or use a browser which doesn't support it, the speaker icon will behave like a normal link and fall back to sending the audio file with the spoken CAPTCHA code to the user, for download or playback in the default application associated with .wav files on their system.
- There is also a small JavaScript fragment associated with the onkeyup event of the CAPTCHA code textbox, which automatically lowercases the user input as they type. This isn't strictly neccesary for the CAPTCHA to function, but it is useful to communicate that the CAPTCHA code isn't case-sensitive to the users. Since solving a CAPTCHA is always a disruption in the user experience (but in principle a smaller one than wading through heaps of bot-submitted spam), it's worth finding as many ways as possible to improve the CAPTCHA usability and minimize the impact it has on the user experience of your form.
- The form also contains a couple of example input fields beside the CAPTCHA, simply to demonstrate how to process user input only if the CAPTCHA is solved correctly, and how to persist the user-submitted values in case they fill out the form but enter an incorrect CAPTCHA code.
ProcessForm.asp
Full Source Code Listing
<%
'Captcha validation
Dim result, codeKey, inputCode
result = False
codeKey = "LanapBotDetectCode"
inputCode = Request("CaptchaCode")
If (Session(codeKey)<>"") Then
code = Session(codeKey)
result = (0 = StrComp(inputCode, code, 1))
'each Captcha code can only be validated once
Session(codeKey) = ""
End If
If result = False Then
first_name = Request("FirstName")
last_name = Request("LastName")
redirect_url = "BotDetectFormDemo.asp?FirstName=" + _
first_name + "&LastName=" + last_name + "&WrongCode=WrongCode"
Response.Redirect redirect_url
End If
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>BotDetect CAPTCHA ASP Form Demo - Protected Page</title>
<link type='text/css' rel='Stylesheet' href='FormStyle.css' />
</head>
<body>
<form name="form1" method="post" id="form1"
action="ProcessForm.asp">
<fieldset id="Properties">
<legend>BotDetect CAPTCHA validation passed!</legend>
<div class="input">
<label for="FirstName">First Name:</label>
<input name="FirstName" id="FirstName" type="text"
class="textbox" readonly="readonly"
value="<% =Request("FirstName") %>" />
</div>
<div class="input">
<label for="LastName">Last Name:</label>
<input name="LastName" id="LastName" type="text"
class="textbox" readonly="readonly"
value="<% =Request("LastName") %>" />
</div>
</fieldset>
<div id="ActionDiv">
<a href="BotDetectFormDemo.asp">Back to the sample form</a>
</div>
</form>
</body>
</html>
Explanation
When users fill out the sample form and submit it, this script is used for submitted data processing. At the very beginning of the file, we validate the CAPTCHA, comparing the user-entered CAPTCHA code to the value stored in Session state. If the CAPTCHA code is not correct, we immediately redirect the user back to the input form. Other field values beside the CAPTCHA are persisted in the querystring for simplicity. There are several things to note about this CAPTCHA validation code:
- Always clear the CAPTCHA code saved in Session state after validation, regardless whether it was successful or not. CAPTCHA images are not meant to be cached or reused, and every form load should use a different CAPTCHA code. If the users solved the CAPTCHA correctly but you need to return them to the input form because one of the other input fields was not entered correctly, remember that the CAPTCHA validation was passed and don't show it on the form anymore. This can be achieved by setting a simple flag (Session("IsHumanUser") = True) and checking it before displaying the CAPTCHA.
- Since the correct CAPTCHA code is stored in ASP Session state, you must ensure that ASP Session state is enabled and working properly for your ASP application. ASP Session state is always persisted in the IIS process memory (which means it can not be shared between processes or servers, i.e. in a web garden or a web farm), and cookie-based (users are returned to their Session data based exclusively on the ASP Session Cookie value).
- In this example, there is only one page protected with the CAPTCHA, which can not be accessed without supplying the correct CAPTCHA code. If you have a series of pages following the input form, and you would like them all to be protected by the same CAPTCHA, you should set a flag after a successful CAPTCHA validation (Session("IsHumanUser") = True) and check it on every subsequent page (redirecting back to the input form if it's not set). Otherwise, malicious users could access these other pages by requesting them directly by Url.
- Users with disabled cookies or without cookie support will always have an empty Session state container, which means they will never be able to solve the CAPTCHA correctly, even if they enter the exact code as shown in the CAPTCHA image. If a significant portion of your users visit your web application with disabled cookies, you might want to investigate some cookieless ASP Session state alternatives. While this will stop bots trying to post spam, it will also stop some potentially useful bots (e.g. Googlebot). Keep in mind that any pages behind the CAPTCHA protection (not the pages where the CAPTCHA is shown, but those accessible only after successfull CAPTCHA validation) will never be crawled, if the CAPTCHA is implemented properly.
- This default CAPTCHA validation code assumes you have the CAPTCHA installed on only one form within the ASP application, since it uses a single Session state key to save the CAPTCHA codes (Session("LanapBotDetectCode")). If you have the CAPTCHA installed on multiple forms within the same application, you should give each of them a unique CAPTCHA identifier (e.g. "Registration" and "Comment"), and append that identifier to the Session state key in the validation code, as well as the querystring of all LanapBotDetectHandler.asp requests (e.g. LanapBotDetectHandler.asp?Command=CreateImage&CaptchaId=Registration). This is necessary to prevent different CAPTCHAs from overwriting each other's codes when different pages are opened at the same time (in multiple tabs of the same browser).
LanapBotDetectHandler.asp
Full Source Code Listing
<%
Dim code, codeKey, codeHash, codeHashKey, captchaId, comCaptcha
'the Captcha code is kept in Session state with this key
codeKey = "LanapBotDetectCode"
codeHashKey = "LanapBotDetectCodeHash"
'if there are multiple Captchas on tn the site, a Captcha id
'is required to distinguish between them; otherwise, it can
'be ignored
captchaId = Request("CaptchaId")
If(captchaId<>"") Then
codeKey = codeKey & "_" & captchaId
End If
If (Request("Command")="CreateImage") Then
'Captcha image generation
'create the Captcha component instance
Set comCaptcha = CreateObject("Lanap.BotDetect")
'process Captcha properties
If (Request("TextStyle")<>"") Then 'set Captcha algorithm
On Error Resume Next
comCaptcha.TextStyle = CLng(Request("TextStyle"))
Err.Clear
End If
If (Request("ImageWidth")<>"") Then 'set Captcha image width
On Error Resume Next
comCaptcha.ImageWidth = CLng(Request("ImageWidth"))
Err.Clear
End If
If (Request("ImageHeight")<>"") Then 'set Captcha image height
On Error Resume Next
comCaptcha.ImageHeight = CLng(Request("ImageHeight"))
Err.Clear
End If
If (Request("CodeLength")<>"") Then 'set Captcha code length
On Error Resume Next
comCaptcha.CodeLength = CLng(Request("CodeLength"))
Err.Clear
End If
If (Request("CodeType")<>"") Then 'set Captcha code type
On Error Resume Next
comCaptcha.CodeType = CLng(Request("CodeType"))
Err.Clear
End If
If (Request("Format")<>"") Then 'set Captcha image format
On Error Resume Next
comCaptcha.Format = Request("Format")
Err.Clear
End If
'set Captcha image Http response headers
Response.Buffer = True
Response.CacheControl = "no-cache, no-store, must-revalidate"
Response.AddHeader "Pragma", "no-cache"
Response.Expires = -1
If (comCaptcha.Format="JPEG") Then
Response.ContentType = "image/jpeg"
ElseIf (comCaptcha.Format="PNG") Then
Response.ContentType = "image/png"
ElseIf (comCaptcha.Format="GIF") Then
Response.ContentType = "image/gif"
ElseIf (comCaptcha.Format="BMP") Then
Response.ContentType = "image/bmp"
End If
'generate the Captcha image binary data
Dim varPicture
varPicture = comCaptcha.CreateImage
'save the Captcha code for sound generation and validation
code = comCaptcha.GetValue
Session(codeKey) = code
'save the code hash for backward compatibility with older
'validation code
codeHash = comCaptcha.GetHashValue
Session(codeHashKey) = codeHash
'send Captcha image binary data to the client
Response.BinaryWrite varPicture
Set comCaptcha = Nothing 'dispose of the Captcha component instance
Response.End
'end Captcha image generation
ElseIf (Request("Command")="CreateSound") Then
'audio Captcha generation
'create the Captcha component instance
Set comCaptcha = CreateObject("Lanap.BotDetect")
'set Http response headers
If (Request.ServerVariables("HTTPS")="off") Then
Response.CacheControl = "no-cache"
Response.AddHeader "Pragma", "no-cache"
Response.Expires = -1
End If
Response.Buffer = True
Response.ContentType = "audio/x-wav"
Response.AddHeader "content-disposition", _
"attachment; filename=captcha.wav"
Response.AddHeader "Content-Transfer-Encoding", "binary"
Response.AddHeader "Connection", "Close"
'generate the audio Captcha binary data from the saved code
code = Session(codeKey)
varSound = comCaptcha.CreateSoundFromCode(code)
'send audio Captcha binary data to the client
Response.BinaryWrite varSound
Set comCaptcha = Nothing 'dispose of the Captcha component instance
Response.End
'end audio Captcha generation
ElseIf (Request("Command")="Validate") Then
'Ajax Captcha validation
Dim result
result = False
If (Session(codeKey)<>"") Then
Dim inputCode
inputCode = Request("Code")
code = Session(codeKey)
result = (0 = StrComp(inputCode, code, 1))
'Ajax validation shouldn't remove the code if successful, so both
'client- and server-side validation can be performed and pass
If (Not result) Then
Session(codeKey) = ""
End If
End If
'Http response headers
Response.Buffer = True
Response.ContentType = "text/javascript"
Response.CacheControl = "no-cache, no-store, must-revalidate"
Response.AddHeader "Pragma", "no-cache"
Response.Expires = -1
Response.AddHeader "Connection", "Close"
'send the JSON validation result to the client
Response.Write "{ 'result': " & LCase(CStr(result)) & " }"
Response.End
'end Ajax Captcha validation
End If
'If neither of the above conditions was met
Response.Status = "400 Bad Request"
Response.End
%>
Explanation
This file is the central ASP module used to access CAPTCHA funtionality from your forms. It provides a simple Http interface to the Lanap.BotDetect COM component which is responsible for all CAPTCHA image and sound generation. All image settings are passed to the handler as querystring parameters. The LanapBotDetectHandler.asp module also exposes a validation Url returning the CAPTCHA validation result in simple JSON format, providing an access point for Ajax CAPTCHA validation.
Due to different requirements for image and sound files sent to the client, different caching Http headers are used. In particular, the CAPTCHA sound Http response detects SSL access to work around a bug in Internet Explorer (where the no-cache headers could prevent the browser form playing the sound file at all).
Note that the CAPTCHA code is generated and saved in Session state during the CAPTCHA image generation, which means the CAPTCHA code will not exist before the CAPTCHA image request is processed. So for example, it is not possible to access the sound CAPTCHA before the image CAPTCHA.
Sicne every ASP application (every IIS virtual directory configured as an application, with it's own Global.asa file, user supplied or default) has it's own Session state, and multiple ASP applications can not share their Session state contents, you can not reuse the LanapBotDetectHandler.asp file between multiple ASP applications – every application must have it's own copy of this file. It is possible to reuse this file between different IIS virtual directories, as long as they are all configured to run within the same ASP application.
BotDetectScript.js
Full Source Code Listing
function LBD_LoadSound(soundPlaceholderId, soundLink) {
if(document.getElementById) {
var i = soundLink.indexOf('&d=');
if (-1 != i) {
soundLink = soundLink.substring(0, i);
}
soundLink = soundLink + '&d=' + LBD_GetTimestamp();
var placeholder = document.getElementById(soundPlaceholderId);
var objectSrc = "<object id='captchaSound'
classid='clsid:22D6F312-B0F6-11D0-94AB-0080C74C7E95'
height='0' width='0' style='width:0; height:0;'><param
name='AutoStart' value='1' /><param name='Volume' value='0'
/><param name='PlayCount' value='1' /><param name='FileName'
value='" + soundLink + "' /><embed id='captchaSoundEmbed'
src='"+ soundLink + "' autoplay='true' hidden='true'
volume='100' type='"+ LBD_GetMimeType() +"'
style='display:inline;' /></object>";
placeholder.innerHTML = "";
placeholder.innerHTML = objectSrc;
}
}
function LBD_GetTimestamp() {
var d = new Date();
var t = d.getTime() + (d.getTimezoneOffset() * 60000);
return t;
}
function LBD_GetMimeType() {
var mimeType = "audio/x-wav";
return mimeType;
}
var LBD_ImgId = null;
var LBD_Img = null;
var LBD_NewImg = null;
var LBD_Parent = null;
var LBD_ImagePrompt = null;
function LBD_ReloadImage(imgId) {
if(imgId) {
LBD_ImgId = imgId;
LBD_Img = document.getElementById(LBD_ImgId);
var src = LBD_Img.src;
var i = src.indexOf('&d=');
if (-1 != i) {
src = src.substring(0, i);
}
var newSrc = src + '&d=' + LBD_GetTimestamp();
LBD_NewImg = document.createElement('img');
LBD_NewImg.onload = LBD_ShowImage;
LBD_NewImg.id = LBD_Img.id;
LBD_NewImg.alt = LBD_Img.alt;
LBD_NewImg.src = newSrc;
LBD_ImagePrompt = document.createElement('span');
LBD_ImagePrompt.appendChild(document.createTextNode('loading...'));
LBD_Parent = LBD_Img.parentNode;
LBD_Parent.removeChild(LBD_Img);
LBD_Parent.appendChild(LBD_ImagePrompt);
}
}
function LBD_ShowImage() {
if(LBD_NewImg && LBD_Parent && LBD_ImagePrompt) {
LBD_Parent.removeChild(LBD_ImagePrompt);
LBD_Parent.appendChild(LBD_NewImg);
}
}
Explanation
This JavaScript file includes functions for CAPTCHA sound playback and CAPTCHA image reloading. Neither of these is required, but using client-side code improves the usability of both.
The CAPTCHA sound can also be played without JavaScript (by downloading the sound file and playing it in the system sound player), but the LBD_LoadSound function allows the sound file to be played directly in the browser and on the page where the CAPTCHA is displayed, using the sound player plugin configured for the browser. Of course, this asumes users have a sound player plugin installed and configured. The CAPTCHA image could also be changed by reloading the whole page, but using the LBD_ReloadImage function to change it asynchronously makes for a better user experience.
Both functions use simple DOM manipulation to add elements to the page dynamically. To play the CAPTCHA sound, we add an <object> element pointing to it and add it to the placeholder <div>. An <embed> element is nested inside it for cross-browser compatibility, even if it's not approved by the W3C standards. When reloading the CAPTCHA image, we replace the old image with a simple prompt while the new image is loading, and show the new CAPTCHA when it's fully loaded and ready.
To avoid any caching problems and ensure a new CAPTCHA sound / image is really loaded from the server when the user clicks the appropriate icon, we append the request timestamp (up to the milisecond) as an additional querystring parameter. A few additional lines are used to check for old timestamp values and replace them (for example, when user clicks the Reload icon multiple times).
FormStyle.css
Full Source Code Listing
body {
background-color: #EEEEFF;
font-family: Verdana, Arial;
font-size: 0.9em;
}
fieldset {
padding: 0 10px 10px 10px;
margin: 11px;
width: 300px;
display: block;
}
div.input {
margin: 7px 0;
}
legend {
padding: 5px;
color: #999999;
}
label {
display: block;
width: 85px;
float: left;
text-align: right;
padding-right: 5px;
}
input.textbox {
width: 170px;
}
input.textboxSmall {
width: 40px;
}
#CodeIncorrectLabel {
color: Red;
}
#CodeCorrectLabel {
color: Green;
}
#Note {
padding: 0;
margin: 11px;
margin-bottom: -7px;
width: 320px;
font-size: 0.8em;
color: Red;
}
#PromptDiv {
padding: 0;
margin: 0;
margin-bottom: 8px;
}
#ActionDiv {
padding: 0 0 10px 10px;
margin: 11px;
margin-right: 0;
width: 314px;
text-align:right;
}
fieldset #ActionDiv{
padding: 0;
margin: 0;
width: auto;
text-align:right;
}
#CaptchaDiv {
margin: 0;
padding: 0;
width:265px;
height:50px;
padding-bottom: 5px;
}
#CaptchaImage {
float: left;
margin: 0;
padding: 0;
width:240px;
height:50px;
}
#CaptchaIcons {
width: 22px;
height: 50px;
float: right;
text-align: left;
margin: 0;
padding: 0;
}
#CaptchaIcons img {
border: 0;
margin: 0;
padding: 0;
padding-bottom: 3px;
}
*html #CaptchaIcons img {
margin-bottom: -2px;
}
.placeholder {
visibility: hidden;
width:0 !important;
height:0 !important;
}
*html .placeholder {
display: none !important;
}
#CaptchaPreviewDiv {
margin: 0;
padding: 0;
padding-bottom: 5px;
}
div.FeaturesInput {
margin: 7px 0;
}
div.FeaturesInput label {
width: 110px;
}
Explanation
This stylesheet defines the appearence of the whole sample form, and not just the CAPTCHA elements – you can copy only the bolded declarations to your stylesheet for that purpose.
We fix the layout of the CAPTCHA elements so the speaker and reload icons are shown vertically to the right of the CAPTCHA image. If you use smaller CAPTCHA image heights and want the icons to be displayed horizontally instead of vertically, just adjust the #CaptchaIcons <div> width. The CAPTCHA image and the parent <div> have their dimensions defined to avoid layout changes when using the Reload button (when the CAPTCHA image is temporarily replaced with a <span> of different dimensions).
The placeholder <div> used to contain the sound element during audio CAPTCHA playback is made invisible by the appropriate declarations, since we want the CAPTCHA sound to play without any visual changes in the page. Since IE 6.0 behaves significantly different from other browsers regarding sound element visibility, a special IE 6.0 -only declaration (starting with *html to filter out other browsers) had to be used.


