.NET Blog

Tony Cavaliere

 
My Favourite Albums
  And the Grappa wins.
E-mail me Send mail
Add to Technorati Favorites AddThis Feed Button

Subscribe to Cynot Why Not


Recent posts

Disclaimer

Hey unlike other bloggers I stand by what I say but just in case. The opinions expressed herein are my own except on Tuesday when the second card is not turned up otherwise it ain't worth squat.

© Copyright 2010

Adding client side reCAPTCHA to your ASP.NET site

CAPTCHA is a challenge-response test to make sure that the user is human rather than some hacker automated software. The user is presented with a graphical image of some text and is required to type the text. If the user’s response is correct then all is well.

reCAPTCHA is unique in that user’s are assisting in the digitization of books, newspapers and radio shows. The service is free and with little effort can be added to your website.

I have been working on a .NET developer community site that requires registration. The site makes use of AJAX and it was decided to that the registration and sign in process would be implement using AJAX. In addition, in order to prevent malicious attacks, reCAPTCHA was added to the registration dialog. The registration dialog is shown below.

registration-reCAPTCHA

The dialog has the typical input fields required for registration but also has the reCAPTCHA to make sure that a human is performing the registration.

This post describes how to add client side reCAPTCHA functionality to your ASP.NET site.

The steps required to add client side reCAPTCHA functionality are:

  1. Register at the reCAPTCHA site. You will receive a public and private API key. The keys are used for encryption. Make sure the private key is hidden away.
  2. Download the ASP.NET server side reCAPTCHA assembly.  
  3. Download the reCAPTCHA client side Javascript. Alternatively, you can just include the Javascript directly for the reCAPTCHA site.
  4. Create an ASP.NET site.
  5. Add Javascript code to create the reCAPTCHA widget.
  6. Add a AJAX enabled web service that accepts the challenge and response data from the reCAPTCHA control.

Steps 1 through 3 are well documented at the reCAPTCHA site and I leave it to the reader to perform these steps. I will also assume that the reader can create a default ASP.NET application.

Adding Javascript to create a reCAPTCHA Widget

Adding client side reCAPTCHA widget to the page is fairly simple. Listing 1 contains the code to display the reCAPTCHA control.

    1 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="recaptcha_client_side._Default" %>

    2 

    3 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    4 

    5 <html xmlns="http://www.w3.org/1999/xhtml" >

    6 <head runat="server">

    7     <title></title>

    8 

    9     <script type="text/javascript" src="jquery-1.3.2.js"></script>

   10     <script type="text/javascript" src="http://api.recaptcha.net/js/recaptcha_ajax.js"></script>

   11 

   12     <script type="text/javascript">

   13       $(function() {

   14         Recaptcha.create("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",

   15           'recaptcha',

   16           {

   17             theme: "red"

   18           }

   19         );

   20       });

   21   </script>

   22 

   23 </head>

   24 <body>

   25     <form id="form1" runat="server">

   26 

   27       <div id="recaptcha"></div>

   28       <input id="getperson" type="button" value="Get Person" />     

   29       <div id="greeting"></div>

   30 

   31     </form>

   32 </body>

   33 </html>

Listing 1: Adding client side reCAPTCHA control

First, as shown on line 10, we need to add the reCAPTCHA Javascript file. I have also added the jQuery Javascript library. Next we add a HTML tag that will host the reCAPTCHA widget. I have chosen a div tag and have given it an id of recaptcha (line 27). Finally, using the reCAPTCHA client side function Recaptcha.create, we create the reCAPTCHA widget (lines 12 to 21). Note that the first parameter, shown in the listing as x’s needs to contain the public key. The second parameter is the id of the tag where the reCAPTCHA widget will be rendered. I have also chosen to use the red theme. This Recaptcha.create API has additional parameters. Refer to the reCAPTCHA client library documentation.

Running the application should render the following in the browser.

recaptcha-simple 

 

Add the AJAX-enabled WCF Service

The next step is to create a server side service that will accept the reCAPTCHA challenge response, as entered by the user. Right-click the asp.net project and select Add->New Item.. menu option. This will display the Add New Item dialog from which you can select the AJAX-enabled WCF Service.

add AJAX-enabled WCF service

The name of the service is ServiceWithRecaptcha.svc. After clicking the Add button, the ServiceWithRecaptcha.svc file(s) will be added the asp.net project. Select the code behind for the ServiceWithRecaptcha.svc. The code editor should display the default  implementation for an AJAX-enabled WCF service. Modify the service as per Listing 2.

    1 using System.Runtime.Serialization;

    2 using System.ServiceModel;

    3 using System.ServiceModel.Activation;

    4 using System.ServiceModel.Web;

    5 using System.Web;

    6 

    7 namespace recaptcha_client_side

    8 {

    9     [ServiceContract(Namespace = "")]

   10     [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

   11     public class ServiceWithRecaptcha

   12     {

   13         [OperationContract]

   14         [WebGet]

   15         public Person GetPerson(string challenge, string response)

   16         {

   17 

   18             Recaptcha.RecaptchaValidator rv = new Recaptcha.RecaptchaValidator

   19             {

   20                 PrivateKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",

   21                 RemoteIP = HttpContext.Current.Request.UserHostAddress,

   22                 Challenge = challenge,

   23                 Response = response

   24             };

   25 

   26             Recaptcha.RecaptchaResponse rr = rv.Validate();

   27             Person p = null;

   28             if (rr.IsValid)

   29             {

   30                 p = new Person { first = "John", last = "Smith"};

   31             }

   32 

   33             return p;

   34         }

   35 

   36     }

   37 

   38     [DataContract]

   39     public class Person

   40     {

   41         [DataMember]

   42         public string first { get; set; }

   43         [DataMember]

   44         public string last { get; set; }

   45     }

   46 }

Listing 2: The AJAX-enabled WCF service. 

The code uses the RecaptchaValidator object which requires a reference to the serve side reCAPTCHA assembly.

In Listing 2, we create a simple function that returns a Person object. The Person class is defined in the code listing and contains only two properties first and last. The class is decorated with the DataContractAttribute indicating that the class is serializable. Both properties are decorated with the DataMemberAttribute meaning that they will be included in the serialization of the object. Remember, unlike web services where properties  are by default included when serialized, in WCF you must opt in.

The actual service class, ServiceWithRecaptcha, is decorated with the ServiceContractAttribute and it’s only function GetPerson is decorated with OperationContractAttribute and WebGetAttribute. This means that the GetPerson function is callable through a HTTP GET method. The GetPerson function accepts two parameters the reCAPTCHA challenge and response from the client side reCAPTCHA API. Prior to returning a person object we call the RecaptchaValidator passing it the private key, shown in x’s, the IP and the challenge and response obtained from the client side (more on this in a bit). The Validate method is then called and if the response, as entered by the user is the same as the challenge then the validator returns true otherwise it returns false. If the validation failed then a null object is returned, otherwise, a Person object is return in JSON serialized format.

Let’s return to the client side code and update it so the the WCF service is called. Listing 3 contains the necessary changes to call the ServiceWithRecaptcha service.

    1 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="recaptcha_client_side._Default" %>

    2 

    3 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    4 

    5 <html xmlns="http://www.w3.org/1999/xhtml" >

    6 <head runat="server">

    7     <title></title>

    8 

    9     <script type="text/javascript" src="jquery-1.3.2.js"></script>

   10     <script type="text/javascript" src="http://api.recaptcha.net/js/recaptcha_ajax.js"></script>

   11 

   12     <script type="text/javascript">

   13       $(function() {

   14 

   15         Recaptcha.create("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",

   16           'recaptcha',

   17           {

   18             theme: "red"

   19           }

   20         );

   21 

   22         $('#getperson').click(function() {

   23           ServiceWithRecaptcha.GetPerson(

   24             Recaptcha.get_challenge(),

   25             Recaptcha.get_response(),

   26             function(result) {

   27               if (result != null) {

   28                 $('#greeting').text('Hello there ' + result.first + ' ' + result.last);

   29               }

   30               else {

   31                 $('#greeting').text('The reCAPTCHA text you entered is wrong.');

   32               }

   33             }

   34           );

   35         });

   36 

   37       });

   38   </script>

   39 

   40 </head>

   41 <body>

   42     <form id="form1" runat="server">

   43 

   44       <asp:ScriptManager runat="server">

   45         <Services>

   46           <asp:ServiceReference Path="~/ServiceWithRecaptcha.svc" />

   47         </Services>

   48       </asp:ScriptManager>

   49 

   50       <div id="recaptcha"></div>

   51       <input id="getperson" type="button" value="Get Person" />     

   52       <div id="greeting"></div>

   53 

   54     </form>

   55 </body>

   56 </html>

Listing 3: Client side modification to invoke the AJAX-enabled WCF service.

Two major changes were made to the aspx page.

The first is the inclusion of the ScriptManager, lines 44 to 48. A reference to the the ServiceWithRecaptcha service is included within the ScriptManager tag. This adds the Javascript proxy code that simplifies the calling of the service. To view the Javascript, while in Visual Studio just select the service file and select the View in Browser menu option and then add /js to the URL. This will display the auto-generated Javascript that can be used to call the service.

The second change required is the Javascript to make the call to the service. This code is shown in lines  22 to 38. I have chosen to use jQuery to attach the click event handler to the the input button. The call to the service is done through the proxy generated code, and in our case is the function, ServiceWithRecaptcha.GetPerson. The two parameters, challenge and response, are obtained by calling the Recaptcha APIs  get_challenge and get_response, respectively. The challenge is encrypted via the public key and can only be decrypt by the private key and hence the call to the service.

Try running the code and entering a valid response to the reCAPTCHA widget. The figure below shows the rendered result. Notice that the text Hello there John Smith is displayed, whereas, if an incorrect response was entered the string The reCAPTCHA text you entered is wrong would be displayed.

recaptcha correct response

Currently rated 2.5 by 2 people

  • Currently 2.5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: ASP.NET
Posted by CynotWhyNot on Monday, June 22, 2009 2:00 PM
Permalink | Comments (73) | Post RSSRSS comment feed

Making sure debug is false in your web.config

This has happened to me more than once. You are working on a web site and are using xcopy for your deployment, whether it be xcopy from an automated process or a manual process and guess what you forgot to set the debug setting in the web.config to false. How many sites are out there with this setting? Is it that big of a deal? Recently, I discovered that not only is your site compiled for debug but also if you use Microsoft Client Side Ajax library (the Javascript included whenever you add ScriptManager tag) it will never get cached. Yes if you are in debug mode each and every visitor to your site will have to upload the entire Microsoft Ajax Client Library as it will not be cached on the client end.

This post will detail an approach to ensuring that the debug setting in the web.config is always deployed with a setting of false.

Listing 1 contains the source code for a console application. The code is extremely simple.

Listing 1 

   10     public class Program

   11     {

   12         public static void Main(string[] args)

   13         {

   14             //Only do this for web.config

   15             if (args.Length == 1)

   16             {

   17                 CultureInfo ci = Thread.CurrentThread.CurrentCulture;

   18                 string path = args[0] as string;

   19                 if (File.Exists(path) && Path.GetFileName(path).ToLower(ci).Equals("web.config"))

   20                 {

   21                     SetDebugTrue(path);

   22                 }

   23             }

   24         }

   25 

   26         //Find the Compilation XML node and set it's debug attribute to false.

   27         //This should be part of the build process to ensure deployed code is in

   28         //release mode.

   29         private static void SetDebugTrue(string path)

   30         {

   31             XmlDocument doc = new XmlDocument();

   32             doc.Load(path);

   33             XmlElement compile = doc.DocumentElement.SelectSingleNode("system.web/compilation") as XmlElement;

   34             if (compile != null)

   35             {

   36                 compile.SetAttribute("debug", "false");

   37                 doc.Save(path);

   38             }

   39         }

   40 

   41     }

The Main accepts a parameter which contains the path of the web.config file. The debug flag is only set if the file is named web.config. The private method SetDebugTrug finds the system.web/compilation XML element and sets the debug attribute to false and that’s it. But let’s go one step further and write a unit test. In order to test this code we will need to set the Program class to public and the Main method to public. By default the Console Application template project sets both of them to internal which makes them difficult to test.

Listing 2 contains the unit test code. I am using nUnit but feel free to use whatever unit testing framework you are comfortable with.

Listing 2

   12     [TestFixture]

   13     public class SetDebugFalseTest

   14     {

   15         [Test]

   16         public void Test_Debug_Attribute_In_WebConfig_Set_To_False()

   17         {

   18             string[] args = { "web.config" };

   19             Assert.That(GetDebugFlag(), Is.EqualTo("true"),"Bad starting web.config. Try rebulding.");

   20             Program.Main(args);

   21             Assert.That(GetDebugFlag(), Is.EqualTo("false"));

   22         }

   23 

   24         private string GetDebugFlag()

   25         {

   26             XmlDocument doc = new XmlDocument();

   27             doc.Load("web.config");

   28             XmlElement compile = doc.DocumentElement.SelectSingleNode("system.web/compilation") as XmlElement;

   29             return compile.GetAttribute("debug");

   30         }

   31     }

For this test to work you will need to add a web.config file, with debug set to true, to the test project and make sure that the Build Action Copy to Output Directory is set to Copy always. The unit test creates an array of string that is passed to the Main method of the Program class (from listing 1). We  then assert that the debug flag is set to false.

The last step in the process is to make sure that this code is executed whenever the site is deployed. Lately, I have been using CruiseControl.NET for my continuous integration progress. Integrating the above in ccnet is quite simple. Listing 3 shows a fragment of the CruiseControl config file that is responsible for running the above code.

Listing 3

  184 <exec>

  185   <executable>sdf.bat</executable>

  186   <baseDirectory>c:\ccnet\projects\gtanetws\src\ccnet</baseDirectory>

  187 </exec>

Here I am using the ccnet exec task to run a batch file. The batch file contains a single line of code that runs the console application produced from listing 1.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: ASP.NET | Tools
Posted by CynotWhyNot on Thursday, April 09, 2009 10:21 AM
Permalink | Comments (71) | Post RSSRSS comment feed

Membership Ajax enabled WCF services: Where is CreateUser?

ASP.NET 2.0 introduced the Membership, Roles and Profile services. Developers could leverage these powerful services and rapidly add membership functionality to their sites. This was a great leap forward but did not address Web2.0, that is, there was no out of the box support for these services from an Ajax perspective. If you wanted to add Ajax type membership functionality the developer had to write ASMX or WCF services that wrapped these API calls. Then came along ASP.NET 3.5. In this release Microsoft shipped a set of Ajax enabled WCF services for the Membership, Roles and Profile services. Adding Ajax support was as simple as adding a few lines of XML to the web.config.

I just recently added Ajax support for the membership services to a local community web site. As advertised, just add a few lines of XML to the web.config and magically you can asynchronously log in or log out. This was wonderful. Then I decided to add support for registering users and then discovered there was no asynchronous equivalent to the CreateUser API. Why was this API not included? Are there security issues exposing this API?

The Ajax Membership API's are located in the assembly C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Web.Extensions.dll. Using every .NET developers trusted friend, reflector, we see that AuthenticationService class contains the following methods;

AuthenticationService     

Where is the CreateUser method? It appears as though it was not included. Why I ask, why was it not included? I guess we still have to resort to writing custom Ajax enabled Membership WCF services!

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: ASP.NET | WCF
Posted by CynotWhyNot on Tuesday, February 17, 2009 12:23 PM
Permalink | Comments (12) | Post RSSRSS comment feed

Adding custom meta tags using MasterPage and ContentPlaceHolders

Meta tags such as description and keywords appear on many web pages and for good reason as it improves SEO (Search Engine Optimization). Adding these meta tags is quite simple, just add the following markup between the <head></head> tag.

    <meta name="description" content="description goes here" />

    <meta name="keywords" content="k1, k2, k3" />

The question arises how does one insert meta tags into aspx pages when they are contained within a master page? The answer is to use a ContentPlaceHolder within the <head></head> of the master page and then add the specific meta tags to each aspx page. ContentPlaceHolders are common place but are typically used within the <form></form> tag. But there is nothing stopping us from inserting a ContentPlaceHolder elsewhere.

The following master page contains two ContentPlaceHolders; one within the <head></head> tag and the second within the <form></form> tag.

    1 <%@ Master Language="C#" AutoEventWireup="true"

    2 CodeBehind="Site.master.cs" Inherits="Meta_Tags_in_Master_Page.Site" %>

    3 

    4 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

    5 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    6 

    7 <html xmlns="http://www.w3.org/1999/xhtml" >

    8 <head runat="server">

    9   <title></title>

   10   <asp:ContentPlaceHolder ID="MetaTags" runat="server" />

   11 </head>

   12 <body>

   13     <form id="form1" runat="server">

   14       <asp:ContentPlaceHolder ID="cphContent" runat="server" />

   15     </form>

   16 </body>

   17 </html>

The next listing shows the aspx markup that is used as the content page for the master page.  

    1 <%@ Page Language="C#" MasterPageFile="~/Site.Master"

    2 AutoEventWireup="true" CodeBehind="default.aspx.cs"

    3 Inherits="Meta_Tags_in_Master_Page._default"

    4 Title="Customized Meta Tags" %>

    5 

    6 <asp:Content ID="MetaTags" ContentPlaceHolderID="MetaTags" runat="server">

    7   <meta name="description" content="description goes here" />

    8   <meta name="keywords" content="k1, k2, k3" />

    9 </asp:Content>

   10 

   11 <asp:Content ID="contentContent" ContentPlaceHolderID="cphContent" runat="server" >

   12   <h2>Adding customized meta tags using MasterPage and ContentPlaceHolders</h2>

   13 </asp:Content>

Within the <asp:Content></asp:Content> tags for the MetaTags ContentPlaceHolder we have essentially two literals; the first for the descriptions meta tag and second for the keywords meta tag. That's all there is to it!

The rendered markup resulting from this aspx page is shown below.

   17 <html xmlns="http://www.w3.org/1999/xhtml" >

   18 <head>

   19 <title>

   20   Customized Meta Tags

   21 </title>

   22   <meta name="description" content="description goes here" />

   23   <meta name="keywords" content="k1, k2, k3" />

   24 </head>

   25 <body>

   26     <form name="aspnetForm" method="post" action="default.aspx" id="aspnetForm">

   27       <div>

   28         <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"

   29         value="/wEPDwUKMTY1NDU2MTA1MmRkdMWL8sX25cN6tOeZt9UyAzgvPQQ=" />

   30       </div>

   31       <h2>Adding customized meta tags using MasterPage and ContentPlaceHolders</h2>

   32     </form>

   33 </body>

   34 </html>

Currently rated 5.0 by 3 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: ASP.NET
Posted by CynotWhyNot on Tuesday, January 20, 2009 8:25 PM
Permalink | Comments (33) | Post RSSRSS comment feed

reCAPTCHA what a brilliant idea!

Most of us have seen them, CAPTCHAs (Completely Automated Public Turing test to tell Computers and Humans Apart). Those weird looking characters that we need to re-type in order to gain access to some web site or resource. Well, they serve a purpose and that is to make sure the user is a real live person rather than some computer script trying to hack a site by perhaps trying to flood the server with large numbers of requests.

I just found out about a brilliant service called reCAPTCHA. It is totally free and with a few lines of code you can add this security feature. But what makes it brilliant is that the service is also digitizing books while it is being used. That's right each time a person types in the characters of the CAPTCHA scanned text from a book is being digitized.

reCAPTCHA

How does that work you may ask?

From the reCAPTCHA site

But if a computer can't read such a CAPTCHA, how does the system know the correct answer to the puzzle? Here's how: Each new word that cannot be read correctly by OCR is given to a user in conjunction with another word for which the answer is already known. The user is then asked to read both words. If they solve the one for which the answer is known, the system assumes their answer is correct for the new one. The system then gives the new image to a number of other people to determine, with higher confidence, whether the original answer was correct.

If you working on a site and require CAPTCHA then please check this service out. It's free and better yet your users will be helping out, digitizing books.

Guess the movie 

Come on... Come on! Do it! Do it! Come on. Come on! Kill me! I'm here! Kill me! I'm here! Kill me! Come on! Kill me! I'm here! Come on! Do it now! Kill me!

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: ASP.NET | HTML
Posted by CynotWhyNot on Thursday, November 13, 2008 2:49 PM
Permalink | Comments (13) | Post RSSRSS comment feed