Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Creating an ASP.NET captcha using jQuery and s3capcha

4.92/5 (12 votes)
12 Aug 2009CPOL5 min read 86.7K   2.3K  
Create an ASP.NET captcha that can be used in Web Forms or with AJAX using s3capcha.

Sceenshot of s3capcha [Asp.net version]

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:

Image 2

  1. It randomizes the images index.
  2. Assigns random numbers to each index, image.
  3. Selects a random index from the list.
  4. 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.
  5. 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.

C#
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):

XML
<?xml version="1.0" encoding="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:

C#
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:

C#
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:

HTML
<!-- html codes -->
<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:

C#
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:

C#
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:

C#
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):

HTML
<!-- HTML Codes -->
<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:

JavaScript
//Javascript codes
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:

JavaScript
//Javascript codes
$(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

  • 2009/08/12
    • First release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)