Introduction
s3capcha is an easy and useful captcha that does not create image files. It is made as a jQuery plug-in, but the author (www.serie3.info) used PHP to code it. I found that it is easy to convert the code to ASP.NET and C#. The PHP code is very simple, but using inline configuration is not good OOP style. I created a structure that is easy to configure and use in applications. Because it does not create any images, s3capcha causes a lower load on the server when creating the captcha, and also we can use this captcha for AJAX or MVC forms.
How s3capcha works
s3capcha uses a simple model for creating a captcha:
- It randomizes the images index.
- Assigns random numbers to each index, image.
- Selects a random index from the list.
- Puts the image number to session and sends a list of images as a radio box to the client; each radio has an image number as value.
- If the users select the right number that matches with the session value, he is validated.
s3capcha uses jQuery to convert the radio box to an image list that can be clicked.
Using the code
The s3capcha
class has three methods, and all of them are static. To preventing using server memory for configuration, I have put all configuration as static variables so I can re-use them.
The first method shuffle
randomizes the array index and returns the result.
public static List<int> shuffle(List<int> input)
{
List<int> output = new List<int>();
Random rnd = new Random();
int FIndex;
while (input.Count > 0)
{
FIndex = rnd.Next(0, input.Count);
output.Add(input[FIndex]);
input.RemoveAt(FIndex);
}
input.Clear();
input = null;
rnd = null;
return output;
}
This function gets the input index list and randomizes it and returns the result. You can modify this code to extend it to shuffle any list.
There are some configurations that need to be set before run the captcha. The original s3capcha
supports two themes for images, and we need to know the theme we are using. There is an XML file used to configure the captcha (in the s3capcha folder):
="1.0"="utf-8"
<s3capcha>
<icons>
<name>apple,cherry,lemon,pear,strawberry</name>
<title>Apple,Cherry,Lemon,Pear,Strawberry</title>
<width>33</width>
<height>33</height>
<ext>jpg</ext>
<folder>fruit</folder>
</icons>
<message>Verify that you are a human not robot, please choose {0}</message>
</s3capcha>
You can see that you can change all the captcha strings and there are no hard-coded strings.
The name
tag contains the image names that will be shown from the specific folder. The title
tag is used for translating images; if you use English, you can use the name tag value, else you can put each image name in your language in this tag.
There are additional images info like width
and height
and also file extension and folder name. The folder name must exist in the icons folder in s3capcha.
The last configuration is the message
tag. You can change it if you need to translate or change text that shows in the client browser. Do not remove the {0} mark. It will be replaced by the name of the image that the user must select.
If you do not want to use an XML file, you can use any method for loading the configuration. For example, you can put values in a web.config as App variables. Just change the private method LoadConfig
that runs for loading the configuration. The default code will load the XML file:
private static bool LoadConfig()
{
string FilePath = "~/s3capcha/config.xml";
FilePath = HttpContext.Current.Server.MapPath(FilePath);
if (System.IO.File.Exists(FilePath))
{
XmlDocument doc = new XmlDocument();
doc.Load(FilePath);
string BaseNode = "/s3capcha/icons/";
XmlNode node = doc.SelectSingleNode(BaseNode + "name");
IconNames = node.InnerText.Split(new char[] { ',' });
node = doc.SelectSingleNode(BaseNode + "title");
IconTitles = node.InnerText.Split(new char[] { ',' });
....
If the configuration is loaded, the function must return true
, else return false
.
The second method is GetHtmlCodes
that renders the HTML code for the captcha. It will use the configuration; if there is no configuration, it will return error:
public static string GetHtmlCodes(string PathTo, out int SessionValue)
{
bool HasValue = false;
if (string.IsNullOrEmpty(Message))
HasValue = LoadConfig();
else
HasValue = true;
if (HasValue)
{
Random Rnd = new Random();
int RandomIndex = Rnd.Next(0,IconNames.Length);
List<int> values = new List<int>();
for(int i = 0; i < IconNames.Length;i++)
values.Add(i);
values = shuffle(values);
string WriteThis = "<div class=\"s3capcha\"><p>" +
string.Format(Message, "<strong>" + IconTitles[values[RandomIndex]] +
"</strong>") + "</p>";
int[] RandomValues = new int[IconNames.Length];
for (int i = 0; i < IconNames.Length; i++)
{
RandomValues[i] = Rnd.Next();
WriteThis += string.Format(RowTemplate,
IconTitles[values[i]], RandomValues[i],
PathTo + "/icons/" + Folder + "/" +
IconNames[values[i]] + "." + Extention,
Width, Height);
}
WriteThis += "<div style=\"clear:left\"></div></div>";
SessionValue = RandomValues[RandomIndex];
return WriteThis;
}
else
{
SessionValue = -1;
return "Invalid data, config file not found";
}
}
PathTo
is the location of the s3capcha folder. And the out
value is what you need to put in the session.
All the server-side code is done, and we can use the s3capcha.js file. If we need to use the captcha in a page, we need to import two JS files: a reference to jquery.js that can downloaded from the jQuery website, and s3capcha.js.
There are two methods that we can use: AJAX or Web Forms.
Using in ASP.NET Web Forms
For using in Web Forms, I created a User Control in the s3capcha folder that can just be dragged to a page, and then we can use the IsValid
property to validate the form.
s3capcha.ascx html code:
<!--
<script language="javascript" type="text/javascript"
src="<%=ResolveClientUrl("~/s3capcha/s3Capcha.js")%>"></script>
<script language="javascript" type="text/javascript">
$(document).ready(function() {$('#capcha').s3Capcha();});
</script>
<div id="capcha">
<asp:Literal ID="CapchaHTML" runat="server"></asp:Literal>
</div>
I added a reference to s3capcha.js, but for the addressing problem, I use ResolveClinetUrl
that changed and returns the relative address.
The Literal
control will include the HTML code. Here is the s3capcha.ascx code-behind:
public void SetIt()
{
int USessionId;
CapchaHTML.Text =
s3capcha.GetHtmlCodes(ResolveClientUrl("~/s3capcha"),
out USessionId);
Session[s3capcha.s3name] = USessionId;
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
SetIt();
}
public bool IsValid
{
get
{
bool res = s3capcha.Verify(Session[s3capcha.s3name],
Request.Form[s3capcha.s3name]);
SetIt();
return res;
}
}
SetIt
will get the HTML code and set the session value. IsValid
will use the helper method verify
of the s3capcha
class that just tests and compares the posted value and the session value.
Create a Web Form like default.aspx and drag s3capcha.ascx to it; and in your postback button function, use IsValid
to validate the user option.
Using in AJAX Forms
For AJAX, we need two ASP.NET pages. One page to get the HTML content and another for submitting the forms content and validating the user input.
I created a page called s3capcha.ashx (Generic Handler) that with each request will return the HTML content of the captcha and also set the Session value:
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/html";
int USession;
context.Response.Write(s3capcha.GetHtmlCodes("../../s3capcha", out USession));
context.Session[s3capcha.s3name] = USession;
context.Response.End();
}
Another generic handler is verify.ashx that used the IsValid
property of our user control:
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
if (s3capcha.Verify(context.Session[s3capcha.s3name],
context.Request.Form[s3capcha.s3name]))
context.Response.Write("Success");
else
context.Response.Write("Fail");
context.Response.End();
}
Because the handler uses session values, we need to inherit from the IRequiresSessionState
interface.
In the client page, we need a place for the captcha on our form. With an AJAX request, we get the result from s3capcha.ashx and add it to this location.
My sample page contains (HTML content):
<!--
<form id="form1" action="verify.ashx" method="post">
<div id="capcha"></div>
<p><input type="submit" value="submit" /></p>
</form>
My forms use a DIV
tag for placing the captcha content. For JavaScript code:
var getCapcha = function() {
$.ajax({
url: 's3capcha.ashx',
cache: false,
success: function(data) {
$('#capcha').html(data).s3Capcha();
}
});
};
getCapcha
uses an AJAX get request. Also because the jQuery cache parameter works in IE, we need to disable it.
Our forms now work, but if you need to submit via AJAX, you can use jQuery forms plug-ins. For now, I create a simple Submit function for this form:
$(document).ready(function() {
getCapcha();
$("form").bind('submit', function() {
$.ajax({
url: 'verify.ashx',
type: 'POST',
data: { 's3capcha': $("input[name=s3capcha]:checked").val() },
cache: false,
success: function(data) {
alert(data);
getCapcha();
}
});
return false;
});
});
See the data section and what I post to the server. When I render the captcha content, a radio input is created with the name "s3capcha". I get the value of the selected item and post it to verify.ashx and get the result. You can use your own method if you like.
Download the source and example and use it. Have any ideas? Post it in the comments section.
History