Simple method to avoid comment spam

Nov 8, 2006

We probably all know about the annoying captcha images that a lot of blogs uses for separating humans from machines (spam robots). I use a captcha image to avoid comment spam on this blog because I get a lot, but I really don’t like to use it. I don’t like the fact that it makes is more difficult for my visitors to write comments, which is my only way of measuring the quality of the individual posts.

What I want is an invisible unobtrusive captcha method that automatically makes sure the user is human. So I wrote a simple method that does just that. It works by adding a small JavaScript to the page that adds a hidden form field when the form is submitted. The value of the hidden field must be the same as a server-side variable to validate. There must also be a property that returns a Boolean value that indicates whether or not the user is human.

/// <summary>

/// Initializes the captcha and registers the JavaScript

/// </summary>

private void InititializeCaptcha()

{

  if (ViewState["captchavalue"] == null)

  {

    ViewState["captchavalue"] = Guid.NewGuid().ToString();

  }

 

  System.Text.StringBuilder sb = new System.Text.StringBuilder();

  sb.AppendLine("function SetCaptcha(){");

  sb.AppendLine("var form = document.getElementById('" + Page.Form.ClientID + "');");

  sb.AppendLine("var el = document.createElement('input');");

  sb.AppendLine("el.type = 'hidden';");

  sb.AppendLine("el.name = 'captcha';");

  sb.AppendLine("el.value = '" + ViewState["captchavalue"] + "';");

  sb.AppendLine("form.appendChild(el);}");

 

  Page.ClientScript.RegisterClientScriptBlock(GetType(), "captchascript", sb.ToString(), true);

  Page.ClientScript.RegisterOnSubmitStatement(GetType(), "captchayo", "SetCaptcha()");

}

 

/// <summary>

/// Gets whether or not the user is human

/// </summary>

private bool IsCaptchaValid

{

  get

  {

    if (ViewState["captchavalue"] != null)

    {

      return Request.Form["captcha"] == ViewState["captchavalue"].ToString();

    }

 

    return false;

  }
}

Examples of use

To use the captcha you have to call InitializeCaptcha from the Page_Load handler. Then just check the IsCaptchaValid property before you save the comment.

protected void Page_Load(object sender, EventArgs e)

{

  InititializeCaptcha();

}

 

/// <summary>

/// Handles the submit buttons onclick event

/// </summary>

void btnSave_Click(object sender, EventArgs e)

{

  if (IsCaptchaValid)

  {

    SaveComment();

  }
}

* $4.95/month BlogEngine.net Hosting – Click Here!

Comments (21) -

 Chris
Chris
11/8/2006 2:05:45 PM #

Wow!

 Johnny Thawte
Johnny Thawte
11/8/2006 3:30:42 PM #

The only thing this doesn't prevent is manual comment spam, which I've gotten a lot of. There's a Wordpress plugin that does something similar to what you are doing here, but then you still end up getting manual spam, which can still be pretty nasty.

Any ideas on how to stop that? Obviously CAPTCHA doesn't work for that either.

 Johnny Thawte
Johnny Thawte
11/8/2006 3:32:01 PM #

Ack. I didn't realize the email address was a public field.

 Martin Knotek
Martin Knotek
11/8/2006 3:57:38 PM #

And what with positive bots?

Example of error in Ajax antispa:
gritechnologies.com/.../NoBot.aspx ?

 spam blocker
spam blocker
11/8/2006 4:28:45 PM #

great!


if any want join to the war against spam
please visit this site - http://www.spam-blocker.biz and please tell what are you thinking about this idea.

 Andrew
Andrew
11/8/2006 10:41:03 PM #

I suppose it stops all those pesky non-javascript users from contributing too!

What happens if someone writes an app that drives IE to sites, then comments? Wouldn't it have JS enabled and pass your tests?

Mads Kristensen
Mads Kristensen
11/8/2006 11:08:54 PM #

Andrew, I think the tradeoff between non-javascript users and spammers are very simple. Almost anybody has enabled JavaScript and the ones who hasn't are aware of it and know they are excluded from a lot of functionality on many many websites. But that's more of a personal choice. This is my opinion on the non-javascript users subject.

Regarding your question about an app that uses IE to submit comments. That scenario will validate the captcha and allow the spammer to submit comments. Luckily for us, spammers don't use that approach that much. It is very time consuming compared to automated post request hammering different servers. But I guess that only time will tell. They get smarter too.

 Mike
Mike
11/9/2006 6:43:39 AM #

Mads,

If I understand this correctly, this works because the spam bots don't render the page in a browser and so don't run javascript. Is this correct?

Steve Harman
Steve Harman
11/9/2006 9:58:40 AM #

Interesting... but it does kinda leave out anyone running with Javascript disabled. Check out the InvisibleCaptchaValidator control that we built for Subtext [http://subtextproject.com]. It works both with and without Javascript (tho it's not invisible if you don't have Javascript enabled).

Anyhow, here is Phil's write up on the control:
haacked.com/.../..._CAPTCHA_Validator_Control.aspx

 John K
John K
3/16/2007 10:23:06 PM #

Interesting idea. I think your solution is a good one.  As for worrying about people who disable Javascript, well - I understand the concern, but you just can't win every battle. I mean, if you're surfing the web and have your Javascript disabled then you should be prepared to have lots of problems with sites!

 jc
jc
3/17/2007 5:30:01 AM #

Shouldnt be a problem to add a degrading approach so if javascript is disabled, just default to a regular captcha.

I like the idea of using viewstate to populate the captcha. I think an even cooler approach would be to set a session variable to a GUID, and use AJAX to call into the server to populate the hidden captcha field on mouseclick of the submit.

mcgurk
mcgurk United States
6/13/2007 11:58:02 AM #

Am I missing something?  What your page is doing is hidden in plain sight.  Seems like a bot could be made to circumvent this by viewing the page source.

Mads Kristensen
Mads Kristensen Denmark
6/13/2007 2:10:48 PM #

mcgurk, you are right that the robots could look for the hidden input field named "captcha". Chances are they don't, but to be sure, you can just give it a random generated name that you store in viewstate. A GUID maybe.

hartvig
hartvig Denmark
6/15/2007 3:52:33 AM #

Requirering javascript enabled is indeed a tradeoff when it comes to accessbility where many people need to have javascript disabled and not because they're lynx or security fanatics. I like the idea and think it would be rock solid with the visible (annoying) captcha fall-back solution. In respect of accessbility this adds additional worries, that blind people wouldn't stand a chance which is why more and more captcha solutions offers audible captcah as well.

Rob
Rob
8/13/2007 4:31:42 PM #

Brilliant.

Rob van der Veer
Rob van der Veer
9/29/2007 11:53:17 AM #

I found a little bug in your code.

When you use this code on a page that has validators, and the validation fails once or twice (or whatever many times), the jscript will add more and more hidden fields with the GUID. This is because the SetCaptcha jscript function will be placed in front of the ClientValidate method in the submit body.

When you call IsCaptchaValid on the server side, the Request.Forms property will contain the GUID multiple times (like "<Guid>,<Guid>") and the validation will fail.

I found 2 solutions: 1) Adjust the javascript to not add another hidden field when one already exists, or 2) Patch the IsCaptchaValid() function to detect multiple post attempts (that is, split the Request.Forms value).

Anthony Grace
Anthony Grace United States
12/2/2007 5:41:12 PM #

Hi Mads,

Do you have a solution for the problem Rob found?

Anthony Smile

Stilgar
Stilgar Bulgaria
12/31/2007 7:10:12 AM #

well Anthony as Rob suggests just truncate the string in IsCaptchaValid

if(Request.Form["captcha"] == null || Request.Form["captcha"].Length <= Guid.Empty.ToString().Length) //which is 36
{
   return null;
}

return Request.Form["captcha"].Substring(0,36) == ViewState["captchavalue"].ToString();

instead of

return Request.Form["captcha"] == ViewState["captchavalue"].ToString();

kunal
kunal India
4/17/2008 11:07:34 AM #

hello sir,
i get following error when i use ur latest updation to the code.
plz update me.
thanks


Compilation Error
Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.

Compiler Error Message: CS0127: Since 'registeruser.InititializeCaptcha()' returns void, a return keyword must not be followed by an object expression

Source Error:



Line 80:             if (Request.Form["captcha"] == null || Request.Form["captcha"].Length <= Guid.Empty.ToString().Length) //which is 36
Line 81:             {
Line 82:                 return null;
Line 83:             }


kunal
kunal India
4/17/2008 11:54:43 AM #

plz post full functional code.
what if  someone checks in .cs file whether the username(a fileld in databse column) exists in the database or not(which means server round trips or page post back) in the signup page while using ur capthca code?

with regards,
kunal

a
a Brazil
7/3/2008 10:20:21 PM #

asdasd

Pingbacks and trackbacks (4)+

Comments are closed

About the author

Mads Kristensen

Mads Kristensen
Program Manager at the Microsoft Web Platform team and founder of BlogEngine.NET.

More...

Month List

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer’s view in any way.