web 2.0

How to Block Spam Comments in BlogEngine.NET

BlogEngine 1.6.1 includes support for ReCaptcha. This tutorial applies to BlogEngine 1.5 and 1.6

Since I started using BlogEngine.NET I noticed that I get a ton of comments that are spam. Yesterday, I discovered a blog post on how to use reCaptcha in BlogEngine.Net by Keith Ratliff. The solution was well done, but I was not happy with the end result because it disabled the Ajax functionality.

Fortunately, while reading the comments of that same blog post by Keith, I found a second solution which was written by Alpha Wu. Alpha's solution maintains the Ajax functionality. The only main difference is that Keith's solution uses reCaptcha (slightly more sophisticated) while Alpha's solution custom draws a verification image. Anyway, while implementing Alpha's code I did make a few slight modifications and I also discovered a bug which prevented it from working correctly on my 1.5.0.7 implementation. Since the original article was written in Chinese and then translated to English I thought I would re-write the original article and include my modifications. So without further ado, here are the steps:

  1. Modify your web.config to enable session state:
    <pages enableSessionState="true" enableViewStateMac="true" enableEventValidation="true">
  2. Create a new page called Captcha.aspx and add it to the root directory of your BlogEngine.net site
  3. Modify the contents of the Captcha.aspx file to read as follows:
    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Captcha.aspx.cs" Inherits="Captcha" %>
  4. Modify the contents of the Captcha.aspx.cs file to read as follows:
    using System;
    using System.Data;
    using System.Configuration;
    using System.Collections;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    
    public partial class Captcha : System.Web.UI.Page {
    	protected void Page_Load(object sender, EventArgs e) {
    		CreateCheckCodeImage(GenCode(4));
    	}
    
    	private string GenCode(int num) {
    		string[] source = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
    		string code = "";
    		Random rd = new Random();
    		int i;
    		for (i = 0; i < num; i++) {
    			code += source[rd.Next(0, source.Length)];
    		}
    		return code;
    
    	}
    
    	private void CreateCheckCodeImage(string checkCode) {
    		if (checkCode.Trim() == "" || checkCode == null)
    			return;
    		Session["AlphaCaptchaCode"] = checkCode;
    		System.Drawing.Bitmap image = new System.Drawing.Bitmap((int)(checkCode.Length * 19), 22);
    		Graphics g = Graphics.FromImage(image);
    		try {
    
    			Random random = new Random();
    
    			g.Clear(Color.White);
    
    			int i;
    			for (i = 0; i < 25; i++) {
    				int x1 = random.Next(image.Width);
    				int x2 = random.Next(image.Width);
    				int y1 = random.Next(image.Height);
    				int y2 = random.Next(image.Height);
    				g.DrawLine(new Pen(Color.Silver), x1, y1, x2, y2);
    			}
    
    			Font font = new System.Drawing.Font("Arial", 14, (System.Drawing.FontStyle.Bold));
    			System.Drawing.Drawing2D.LinearGradientBrush brush = new System.Drawing.Drawing2D.LinearGradientBrush(new Rectangle(0, 0, image.Width, image.Height), Color.Blue, Color.DarkRed, 1.2F, true);
    			g.DrawString(checkCode, font, brush, 4, 1);
    
    			g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1);
    			System.IO.MemoryStream ms = new System.IO.MemoryStream();
    			image.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
    			Response.ClearContent();
    			Response.ContentType = "image/jpg";
    			Response.BinaryWrite(ms.ToArray());
    
    		}
    		catch {
    			g.Dispose();
    			image.Dispose();
    		}
    
    	}
    }
  5. Modify the CommentView.ascx file which is located in the User Controls folder. Find the section that starts with '<span class="bbcode" title="BBCode tags"><%=BBCodes() %></span>' and paste this code directly above it:
    <label for="<%=txtCaptcha.ClientID %>">Captcha*</label>
    <img src="/Captcha.aspx" alt="Click to change captcha" style="width: 82px; height: 23px" onclick="this.src=RefreshCaptcha(this.src)" />
    <asp:TextBox runat="Server" ID="txtCaptcha" TabIndex="6" MaxLength="4" Width="60px" onblur="DoCheckCaptcha()"/>
    <span id="CaptchaMsg"></span>
    <asp:RequiredFieldValidator ID="RequiredFieldValidator4" runat="server" ControlToValidate="txtCaptcha" ErrorMessage="<%$Resources:labels, required %>" Display="dynamic" ValidationGroup="AddComment" />
    <br />

    Make sure you also update the tab index of the surrounding controls so you can make sure things flow properly when you hit the tab key.
  6. Modify the CommentView.ascx located in the User Controls folder. The following modification will validate the text that you entered in the txtCaptcha textbox when you click the save button:
    For BlogEngine 1.5 use this:
    <input type="button" id="btnSaveAjax" value="<%=Resources.labels.saveComment %>" onclick="if(Page_ClientValidate('AddComment')&&checkCaptchaResult){BlogEngine.addComment()}" tabindex="7" />
    For BlogEngine 1.6 use this:
    <input type="button" id="btnSaveAjax" value="<%=Resources.labels.saveComment %>" onclick="if(Page_ClientValidate('AddComment')&&checkCaptchaResult){BlogEngine.validateAndSubmitCommentForm()}" tabindex="7" />
  7. Modify the CommentView.ascx file to include this JavaScript code. This function DoCheckCaptcha will verify the image against the text you typed in and the RefreshCaptcha will allow you to get a new captcha image when you click on the control.
    <script type="text/javascript">
    	function DoCheckCaptcha() {
    		var code = document.getElementById("<%=txtCaptcha.ClientID %>").value;
    		checkCaptcha(code);
    	}
    	var checkCaptchaResult = false;
    	function ReceiveServerData(CheckResult) {
    		document.getElementById("CaptchaMsg").innerHTML = "";
    		if (CheckResult == 1) {
    			checkCaptchaResult = true;
    			document.getElementById("CaptchaMsg").innerHTML = "<font color=green>OK</font>";
    		}
    		else if (CheckResult == -1) {
    			checkCaptchaResult = false;			
    		}
    		else {
    			checkCaptchaResult = false;
    			document.getElementById("CaptchaMsg").innerHTML = "<font color=red>Sorry that is incorrect.</font>";
    		}
    	}
    	function RefreshCaptcha(url) {
    		if (url.toString().indexOf("?", 0) > 0) {
    			url = url.toString().substring(0, url.toString().indexOf("?", 0)) + "?" + new Date().toUTCString();
    		}
    		else {
    			url = url.toString() + "?" + new Date().toUTCString();
    		}
    		return url;
    
    	}
    </script>
  8. In the CommentView.ascx.cs (Code Behind), add the following code to the beginning of the RaiseCallbackEvent function
    if (eventArgument.Length < 1)
      {
          _Callback = "-1";
          return;
      }
      if (eventArgument.LastIndexOf("-|-") < 0)
      {
          string img = Session["AlphaCaptchaCode"].ToString().ToLower(); ;
          if (eventArgument.ToLower().Equals(img))
          {
              _Callback = "1";
          }
          else
          {
              _Callback = "0";
          }            
          return;
      }
  9. Finally, in the Page_Load of the CommentView.ascx.cs file add the following code right below the line that starts with the comment //InitializeCaptcha();
    string cbReference = Page.ClientScript.GetCallbackEventReference(this, "CheckResult", "ReceiveServerData", "");  
    string callbackScript = "function checkCaptcha(CheckResult){" + cbReference + ";}";                                      
    Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "checkCaptcha", callbackScript, true); 

If everything works you should end up with a comment form that looks like this:

I would also recommend that you install the Commenter Extension if you are using BlogEngine.NET. If you use it in conjunction with this technique you will see a huge reduction in spam. Yes, I know this is a lot of work but the fact that I get no more spam makes it a worthwhile endeavor. To see this code in action, just leave me a comment!


Update 3/15/2010:  I made a few changes to the Captcha code. The letters are now of varying color and size. I also made the vertical and horizontal lines in the background instead of the random scribble pattern show in the screenshot above. Here is the code if you are interested.

Captcha.aspx (123.00 bytes) Captcha.aspx.cs (2.87 kb)

Comments

topsy.com , on 1/19/2010 1:05:12 PM Said:

pingback

Pingback from topsy.com

Twitter Trackbacks for
        
        Code Capers | How to Block Spam Comments in BlogEngine.NET
        [codecapers.com]
        on Topsy.com

beefarino United States, on 1/19/2010 1:18:39 PM Said:

beefarino

Oh dear God thank you!  I will be putting this on my blog tonite!

machewd United States, on 1/19/2010 11:15:57 PM Said:

machewd

Thanks so much for this solution!  I tried to solve this a year ago, but couldn't.  I really appreciate you posting this.

uberVU - social comments , on 1/20/2010 4:43:52 AM Said:

trackback

Social comments and analytics for this post

This post was mentioned on Twitter by mikeceranski: blogged:  How to Block Spam Comments in #BlogEngine.NET http://goo.gl/fb/emfi #programming

Harald Germany, on 1/21/2010 6:49:14 AM Said:

Harald

Hallo,
I have installed the captcha the way you told, but there is only an image-symbol and not the captcha-image.
My question: Is it necessary for running Ajax to add parameters in the web.config or IIS? My test-environement on the localhost(PC) is IIS 7.0, ASP.Net 2.0 and BlogEngine 1.5 last version.
Thanks for reply - Harald  

Mike United States, on 1/21/2010 10:32:57 AM Said:

Mike

Harald,
  The image should load regardless of the fact that you are using Ajax or Post Back. In step 5 make sure you img tag points to the Captcha image class you created. For example my code uses <img src="/Captcha.aspx" which corresponds to Captcha.aspx and Captcha.aspx.cs that resides in the root folder of my website.
Thanks,
    Mike

Steven H United States, on 1/22/2010 10:26:58 AM Said:

Steven H

Oh man this is excellent. I got dozens of spam a day on my site. Cheers to you sir!

Code Capers , on 1/23/2010 9:54:40 AM Said:

trackback

Migrating From Blogger to BlogEngine.Net

Migrating From Blogger to BlogEngine.Net

Harald Germany, on 1/24/2010 9:01:01 AM Said:

Harald

Sorry, may-be it is a stupid question:
I have edited all the coding you mentioned. Is it now necessary for running to rebuild the BlogEngine.Core.DLL in the BIN-folder? (In that case I would have to buy a MS Visual Studio - Programm instead of my VWD.)
Greetings from old Germany ...

Mike Ceranski United States, on 1/24/2010 10:35:47 AM Said:

Mike Ceranski

Harald,
   There is no recompile required. I have made many changes to my comment and post pages and I never recompiled any of the source.
Thanks,
   Mike

Al Bsharah United States, on 1/24/2010 9:08:19 PM Said:

Al Bsharah

NICE job on this!  Thanks for commenting on my post about addressing SPAM on BlogEngine, might not have seen it otherwise.

Couple of quick comments regarding the write-up:
1)  #3 and #4 should be reversed (.aspx vs .aspx.cs)
2)  Maybe note that CommentView files are located in the "User Controls" folder.  Some might be looking at the "theme" versions of the file.

I wrote another article pointing to this one...
al.bsharah.com/.../...nstallation-Reduce-SPAM.aspx

Seems to be working great!
Cheers,
Al

Mike Ceranski United States, on 1/24/2010 11:16:04 PM Said:

Mike Ceranski

Thanks for the feedback Al. I made the changes as you recommended. I will check out your article as well.

Keith Ratliff United States, on 1/26/2010 12:32:11 AM Said:

Keith Ratliff

For the record, my solution (that you linked first) doesn't break the preview functionality - but it does kill some of the ajax - most notably when the post is posted.

I prefer recaptcha for a few reasons, not the least is that it helps to transcript old texts in to documents, but of course, how important each is up to the user. =)

Glad to see you have a workable solution. If you don't mind a spot of feedback, you should add each letter individually to the image and randomize the Y element a little and even the scale a little. Small variants are usually not too hard for humans, but are harder for machines.

One other touch I would recommend would be to avoid ambiguous characters, such as "l" "1" "I" in the images.

Regardless, I'll link your post officially in to my original writeup so you have postback. (Cause one of the other changes I made when I made my theme is that all URLs in posts are "nofollow" on my theme.)

Mike United States, on 1/26/2010 7:55:01 AM Said:

Mike

Hi Keith,
   Thanks for leaving a comment. I updated the article to remove the comment about your solution breaking the live preview.

Also, you have some great suggestions on how to improve the captcha image. I appreciate the feedback and I will try to incorporate these changes in when time permits.
Thanks Again,
    Mike

Bryan Nash United States, on 1/27/2010 9:31:21 AM Said:

Bryan Nash

Yeah I supposed captcha would be effective to stop spam. Thanks for the share.

Harald Germany, on 1/27/2010 5:54:00 PM Said:

Harald

Hope, this will be my last problem:
I input a comment together with name, e-mail and captcha and will store it, but the application hangs without storing data.
Any idea?

Michael Ceranski United States, on 1/27/2010 6:31:53 PM Said:

Michael Ceranski

Hi Harald,
    I am sending you a copy of the files I am using. You can compare them and see if you are missing something. Also, make sure the account running IIS had write privileges to the App_Data directory.

Let me know if you get it working.

Wayne John United States, on 1/27/2010 7:21:33 PM Said:

Wayne John

I used to run BlogEngine and left after I discovered a tool that allows spammers to blast their comment spam to any BE based blog.  I alerted everyone on Codeplex when I found it, and they quickly turned a fix out for it, but I felt the fix was not good enough.

In my opinion, and I could be quite wrong, I thought it best to provide dynamic names to the form elements.  Everyone knows that each instance of BE will have three textboxes at the bottom of each post that are named the same each and every time.  If you provide a screen in the admin that allows the owner to dynamically name these elements, all those automated tools would then be rendered useless (for the most part).

I run Wordpress now, and things aren't so bad on this side of the fence.

Cheers!

Harald Germany, on 1/28/2010 11:38:23 AM Said:

Harald

Hallo Mike,
many thanks for your help!
I had in CommentView.ascx the javascript inserted between the last two asp-PlaceHolders. When I put the script to the end - as you did - all is running now Wink
Wish I could help you too, but it is a long road for me to have similar experience ....

Michael Ceranski United States, on 1/28/2010 11:44:17 AM Said:

Michael Ceranski

No problem Harald. I am glad you got it working!

Al Bsharah , on 2/2/2010 4:57:15 PM Said:

trackback

Add CAPTCHA to Your BlogEngine.NET Installation - Reduce SPAM!

Add CAPTCHA to Your BlogEngine.NET Installation - Reduce SPAM!

efek blogging terhadap motivasi diri Indonesia, on 2/9/2010 1:42:52 PM Said:

efek blogging terhadap motivasi diri

I have installed the captcha the way you told, but there is only an image-symbol and not the captcha-image.
My question: Is it necessary for running Ajax to add parameters in the web.config or IIS? My test-environement on the localhost(PC) is IIS 7.0, ASP.Net 2.0 and BlogEngine 1.5 last version.
Thanks for reply - Harald  

Joshua United States, on 2/19/2010 5:18:51 PM Said:

Joshua

Thank you so much!  I just used your instructions on my v1.6 BlogEngine.Net site and it works perfectly!  You rock!

Nullstring Republic of the Philippines, on 2/19/2010 8:50:41 PM Said:

Nullstring

I haven't implemented the codes. But looks promising.

BotSpammers are getting smart now so those letters are easy as reading like a 1st grader student. and that "OK" is very bad, because spammers could use brute force attack and wait for that "OK" response.

I saw Al's discussion on codeplex I also commented there if it possible to use Animals as a CAPTCHA because I once saw that in DigInfo news and looks very interesting.

PDF here www.fp6-noah.org/.../enhanced_captchas_cms06.pdf

Nullstring Republic of the Philippines, on 2/19/2010 10:14:51 PM Said:

Nullstring

Hey Michael.

I found the video. Sorry, not DiggInfo News, but in NewScientisVideo

http://www.youtube.com/watch?v=dE4arbdM-D4

David Wynne United Kingdom, on 2/21/2010 8:45:41 AM Said:

David Wynne

Just taking a look through your code above - Captcha.aspx.cs line 32.
   if (checkCode.Trim() == "" || checkCode == null)

You need to swap that check - if checkCode is null, then trying to call Trim() on it will throw a NullReferenceException before you've checked it for null.

Also your code presumes that the blog is being run in either the root of a site or it's own Virtual Directory.  If it's not then /Captcha.aspx is not found and a broken image is displayed.  Small point but it caught me out first time and maybe the cause for others who are not seeing images.

Thanks for the code though and I hope my comments are of some help!

noob noob United States, on 2/21/2010 1:29:02 PM Said:

noob noob

Hi: I'm also not getting the captcha image. I have BE 1.6 in an application off the root of the web, and when I get properties on the captcha it's pointing to "www.myweb.com/captcha.aspx" instead of "www.myweb.com/myapp/captcha.aspx". I think this is because it's a plain img tag so it's not picking up the app path?

I tried hard-coding the full path into the tag but that's not working either (i didn't really expect it to). I'll keep playing with it but if you have any suggestions please let me know.

mikeceranski United States, on 2/21/2010 3:35:27 PM Said:

mikeceranski

If you are using a virtual path then you will probably need to change the image tag:

<img src="/<virtual directory>/Captcha.aspx" alt="Click to change captcha" style="width: 82px; height: 23px" onclick="this.src=RefreshCaptcha(this.src)" />

David Wynne United Kingdom, on 2/21/2010 5:39:50 PM Said:

David Wynne

I believe changing it to "../Captcha.aspx" will work in all cases regardless of where the blog is hosted.

Alex Meyer-Gleaves , on 2/25/2010 9:39:25 AM Said:

trackback

Comment Spam protection for BlogEngine.NET

Comment Spam protection for BlogEngine.NET

ZBuggies , on 2/25/2010 11:50:21 AM Said:

trackback

How to Block Spam Comments in BlogEngine.NET

How to Block Spam Comments in BlogEngine.NET

Olaf Rabbachin Germany, on 3/14/2010 2:25:19 PM Said:

Olaf Rabbachin

Hey Mike,

thanks for the how-to - worked like a charm! Just wondering - did you make any modifications to the generated Captcha? The one being used here sure is different (varying positions and font-sizes) than what's being created by the code you're writing about ..?

Cheers!

mikeceranski United States, on 3/14/2010 9:03:56 PM Said:

mikeceranski

I did modify the captcha image since I originally posted this article. I changed the colors, and made the characters different sizes just to make it harder to hack. I can post the code if you are interested.

Olaf Rabbachin Germany, on 3/15/2010 1:50:37 AM Said:

Olaf Rabbachin

Hi Mike,

I'd appreciate it!

mikeceranski United States, on 3/15/2010 9:06:54 AM Said:

mikeceranski

Olaf, the new code has been attached to the end of the article. Enjoy!

South Florida Web Design United States, on 4/1/2010 2:24:18 PM Said:

South Florida Web Design

Thanks for posting this guide Mike! I was able to minimize the spam on my blog!Cheers

Nishanth Nair India, on 4/3/2010 11:18:32 AM Said:

Nishanth Nair

Thanks a lot Mike! Your solution works like a charm.. i was flooded with spam in the past couple of weeks.. Keep up the good work!! Thanks once again!

Eyre Republic of the Philippines, on 4/4/2010 1:39:38 PM Said:

Eyre

Hi Mike,

I've just installed the captcha to my blog using the instructions you provided and it worked like a charm.
Thanks a lot for this.

a928isreborn , on 4/6/2010 7:40:26 AM Said:

trackback

Time to kill off the spammers...

Time to kill off the spammers...

James Richards United States, on 4/11/2010 2:31:02 PM Said:

James Richards

Just skimming through the comments again. With regards to David's comment:

"Just taking a look through your code above - Captcha.aspx.cs line 32.
   if (checkCode.Trim() == "" || checkCode == null)

You need to swap that check - if checkCode is null, then trying to call Trim() on it will throw a NullReferenceException before you've checked it for null."

Even better would be to replace the line with:

if (String.IsNullOrEmpty(checkCode))

Also, it's on line 45 of the new version.

Thanks again Mike!

Mark Broe.NET , on 4/19/2010 11:58:19 AM Said:

trackback

Adding CAPTCHA validation to BlogEngine.Net

Adding CAPTCHA validation to BlogEngine.Net

Garth Arnold United States, on 4/24/2010 7:59:57 PM Said:

Garth Arnold

Thanks for sharing this, very useful.  A comment - if you've installed BE other than to the root of your website, edit line 2 in step 5 (the location of /Captcha.aspx) to point to that location.

Yeejie's Blog , on 4/25/2010 8:03:41 AM Said:

trackback

Captcha in BlogEngine

Captcha in BlogEngine

BlogEngine.NET , on 5/10/2010 4:54:59 PM Said:

trackback

Updates and Links (May 2010)

Updates and Links (May 2010)

85.tijuanareader.com , on 5/20/2010 3:20:05 PM Said:

pingback

Pingback from 85.tijuanareader.com

K3500 Parts S15 Jimmy Gmc Savana, 2007 Gmc Savana 3500

Comments are closed