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:
- Modify your web.config to enable session state:
<pages enableSessionState="true" enableViewStateMac="true" enableEventValidation="true">
- Create a new page called Captcha.aspx and add it to the root directory of your BlogEngine.net site
- Modify the contents of the Captcha.aspx file to read as follows:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Captcha.aspx.cs" Inherits="Captcha" %>
- 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();
}
}
}
- 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.
- 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" />
- 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>
- 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;
}
- 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)