Print Version Email Page Add to Favorites Comments Alert Me Add to My Links

Tuesday, June 17, 2008

CAPTCHA for SharePoint Blog

Introduction
The word ‘CAPTCHA’ stands for Completely Automated Public Turing test to tell Computers and Humans Apart. Most World Wide Web users will have seen this kind of test in the form of a picture of a word (usually distorted), which the user must type into an input box to prove that they are a real person, and not just a spambot, or some other computerized agent trawling the Web for exploits.

A CAPTCHA is a challenge-response test most often placed within web forms to determine whether the user is human. The purpose of CAPTCHA is to block form submissions from spambots – automated scripts that harvest email address from publicly available web forms.

Implementing CAPTCHA For SharePoint

Create HTTPHandler for Generating Image
The easiest way to create a custom HttpHandler component is to create a source file with an .ashx extension. You must then add a @WebHandler directive to the top of the .ashx file, along with a class definition that implements the IHttpHandler interface. Any class that implements the IHttpHandler interface must provide an implementation of the IsReusable method and the ProcessRequest method. If you want to be able to program against the Windows SharePoint Services object model from inside the HttpHandler component, you can also add an @Assembly directive to reference the Microsoft.SharePoint assembly.
1. Create a folder inside LAYOUTS directory. For e.g.: C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS\Sprint
2. Create a file named captcha.ashx inside that folder.
3. Include the following code in captcha.ashx


<%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ WebHandler Language="C#" Class="captcha" %>

using System;
using System.Web;
using System.Drawing;
using System.Web.SessionState;
using System.Data;
using System.Configuration;
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.Drawing2D;

public class captcha : IHttpHandler, IRequiresSessionState
{

public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "image/jpeg";
ADSSAntiBot captcha = new ADSSAntiBot();
string str = captcha.DrawNumbers(5);
if (context.Session[ ADSSAntiBot.SESSION_CAPTCHA] == null) context.Session.Add(ADSSAntiBot.SESSION_CAPTCHA, str);
else
{
context.Session[ ADSSAntiBot.SESSION_CAPTCHA] = str;
}
Bitmap bmp = captcha.Result;
bmp.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
}



public bool IsReusable {
get {
return true;
}
}

}

public class ADSSAntiBot
{
public static string SESSION_CAPTCHA = "CAPTCHA";


const int default_width = 135;
const int default_height = 35;

protected Bitmap result = null;

public int Width;
public int Height;

public ADSSAntiBot()
{
InitBitmap(default_width, default_height);
rnd = new Random();
}

public ADSSAntiBot(int width, int height)
{
InitBitmap(width, height);
}

protected void InitBitmap(int width, int height)
{
result = new Bitmap(width, height);
Width = width;
Height = height;
rnd = new Random();
}



public PointF Noise(PointF p, double eps)
{
p.X = Convert.ToSingle(rnd.NextDouble() * eps * 2 - eps) + p.X;
p.Y = Convert.ToSingle(rnd.NextDouble() * eps * 2 - eps) + p.Y;
return p;
}

public PointF Wave(PointF p, double amp, double size)
{
p.Y = Convert.ToSingle(Math.Sin(p.X / size) * amp) + p.Y;
p.X = Convert.ToSingle(Math.Sin(p.X / size) * amp) + p.X;
return p;
}



public GraphicsPath RandomWarp(GraphicsPath path)
{
// Add line //
int PsCount = 10;
PointF[] curvePs = new PointF[PsCount * 2];
for (int u = 0; u < PsCount; u++)
{
curvePs[u].X = u * (Width / PsCount);
curvePs[u].Y = Height / 2;
}
for (int u = PsCount; u < (PsCount * 2); u++)
{
curvePs[u].X = (u - PsCount) * (Width / PsCount);
curvePs[u].Y = Height / 2 + 2;
}

path.AddLines(curvePs);

//
double eps = Height * 0.05;

double amp = rnd.NextDouble() * (double)(Height / 3);
double size = rnd.NextDouble() * (double)(Width / 4) + Width / 8;

double offset = (double)(Height / 3);


PointF[] pn = new PointF[path.PointCount];
byte[] pt = new byte[path.PointCount];

GraphicsPath np2 = new GraphicsPath();

GraphicsPathIterator iter = new GraphicsPathIterator(path);
for (int i = 0; i < iter.SubpathCount; i++)
{
GraphicsPath sp = new GraphicsPath();
bool closed;
iter.NextSubpath(sp, out closed);

Matrix m = new Matrix();
m.RotateAt(Convert.ToSingle(rnd.NextDouble() * 30 - 15), sp.PathPoints[0]);

//m.Shear(Convert.ToSingle( rnd.NextDouble()*offset-offset ),Convert.ToSingle( rnd.NextDouble()*offset-offset/2 ));
//m.Shear(1,1);

//m.Scale(0.5f + Convert.ToSingle(rnd.NextDouble()), 0.5f + Convert.ToSingle(rnd.NextDouble()), MatrixOrder.Prepend);

m.Translate(-1 * i, 0);

sp.Transform(m);

np2.AddPath(sp, true);
}




for (int i = 0; i < np2.PointCount; i++)
{
//pn[i] = Noise( path.PathPoints[i] , eps);
pn[i] = Wave(np2.PathPoints[i], amp, size);
pt[i] = np2.PathTypes[i];
}

GraphicsPath newpath = new GraphicsPath(pn, pt);

return newpath;

}

Random rnd;


public string DrawNumbers(int len)
{
string str = "";
for (int i = 0; i < len; i++)
{
int n = rnd.Next() % 10;
str += n.ToString();
}
DrawText(str);
return str;
}

public void DrawText(string aText)
{

Graphics g = Graphics.FromImage(result);
int startsize = Height;
Font f = new Font("Verdana", startsize, FontStyle.Bold, GraphicsUnit.Pixel);

do
{
f = new Font("Verdana", startsize, GraphicsUnit.Pixel);
startsize--;
} while ((g.MeasureString(aText, f).Width >= Width) || (g.MeasureString(aText, f).Height >= Height));
SizeF sf = g.MeasureString(aText, f);
int width = Convert.ToInt32(sf.Width);
int height = Convert.ToInt32(sf.Height);

int x = Convert.ToInt32(Math.Abs((double)width - (double)Width) * rnd.NextDouble());
int y = Convert.ToInt32(Math.Abs((double)height - (double)Height) * rnd.NextDouble());

//////// Paths ///
GraphicsPath path = new GraphicsPath(FillMode.Alternate);

FontFamily family = new FontFamily("Verdana");
int fontStyle = (int)(FontStyle.Regular);
float emSize = f.Size;
Point origin = new Point(x, y);
StringFormat format = StringFormat.GenericDefault;

path.AddString(aText, family, fontStyle, emSize, origin, format);

path = RandomWarp(path);
/// Path ///

g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
Rectangle rect = new Rectangle(0, 0, Width, Height);
g.FillRectangle(new System.Drawing.Drawing2D.LinearGradientBrush(rect, Color.White, Color.LightGray, 0f), rect);
//g.DrawString(aText, f, new SolidBrush(Color.Black), x, y);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.FillPath(new SolidBrush(Color.Black), path);


// Dispose //
g.Dispose();
}

public Bitmap Result
{
get
{
return result;
}
}
}


Now, this HTTPHandler is accessible to any site in the farm by using a site-relative path. http://MyWebServer/_layouts/ACME/captcha.ashx
Note:Please make sure that you have unblocked .ashx extension for your web application from Central Administration.
Unblock .ashx extension for your web application from Central Administration
1. Open SharePoint 3.0 Central Administration.
2. Click Operations tab.
3. Click Blocked file types under Security Configuration.
4. Select your web application.
5. Remove ashx file extension from the list.
6. Click OK.
Implement SharePoint WebPart for CAPTCHA
1. Create a Class Library Project using Visual Studio 2005.
2. Add a reference to:
a. System.Web
b. Microsoft.SharePoint




using System;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using Microsoft.SharePoint;

using Microsoft.SharePoint.WebPartPages;



namespace CaptchaWebPart

{

public class CaptchaWebPart : WebPart

{

private Image imgCaptcha;

private TextBox txtCaptchaNumber;

private RequiredFieldValidator rfv;

private CustomValidator captchaValidator; // Performs user-defined validation on an input control.



///

/// Override the ASP.NET Web.UI.Controls.CreateChildControls method

/// to create the objects for the Web Part's controls.

///


protected override void CreateChildControls()

{

// Get the SPWeb

//SPSite siteColl = SPContext.Current.Site;

SPWeb site = SPContext.Current.Web;



// table Formatting

Controls.Add(new LiteralControl("<_table border="\">"));

Controls.Add(new LiteralControl("<_tr><_td>"));



// CAPTCHA Image

imgCaptcha = new Image();

imgCaptcha.ID = "imgCaptcha";

imgCaptcha.ImageUrl = site.Url + "/_layouts/Sprint/captcha.ashx";

imgCaptcha.AlternateText = "If you can't read this number refresh your screen.";

Controls.Add(imgCaptcha);



Controls.Add(new LiteralControl(""));

Controls.Add(new LiteralControl("<_tr><_td>"));

Controls.Add(new LiteralControl("<_strong>Enter the code shown above: <_span style="\">*<_br>"));



// Textbox for user to enter CAPTCHA Text

txtCaptchaNumber = new TextBox();

txtCaptchaNumber.ID = "txtCaptchaNumber";

txtCaptchaNumber.EnableViewState = false;

Controls.Add(txtCaptchaNumber);



// Required Field Validator

rfv = new RequiredFieldValidator();

rfv.ControlToValidate = "txtCaptchaNumber";

rfv.Display = ValidatorDisplay.Dynamic;

rfv.Text = "*";

Controls.Add(rfv);



captchaValidator = new CustomValidator();

captchaValidator.ServerValidate += new ServerValidateEventHandler(captchaValidator_ServerValidate);

captchaValidator.Display = ValidatorDisplay.Dynamic;

captchaValidator.Text = "Incorrect Code";

Controls.Add(captchaValidator);



Controls.Add(new LiteralControl("<_br><_i>(Note: If you cannot read the numbers in the above image, reload the page to generate a new one.)"));

Controls.Add(new LiteralControl(""));

Controls.Add(new LiteralControl(""));

}



///

/// Renders the HTML for the body of a Web Part to the client.

///


///

protected override void RenderWebPart(HtmlTextWriter output)

{

RenderChildren(output);

}



///

/// Raises the ServerValidate event for the CustomValidator control.

///


///

///

void captchaValidator_ServerValidate(object source, ServerValidateEventArgs args)

{

if (txtCaptchaNumber.Text == (string)this.Page.Session["CAPTCHA"])

{

args.IsValid = true;

}

else

{

args.IsValid = false;

}

}



}

}


Note: Please remove _ from above code appearing before HTML Tags (for e.g.: _table, _td, _tr)

3. Sign the assembly with Strong Key Name file.
4. Compile the code.

Configuring Portal to use Captcha WebPart
1. Navigate to Blog’s site.
2. Click on Comments link under any blog post. (http://provpc:24106/ACME/Lists/Posts/Post.aspx?ID=1#Comments&PageView=Shared)
3. Click Site Actions -> Edit Page.
4. Click Add a Web Part.
5. Select CaptchaWebPart from category Miscellaneous under All Web Parts.
6. Click edit -> Modify Shared Web Part.
7. Select Chrome Type as None.
8. Click OK.
9. Align CaptchaWebPart below New Comment WebPart.
10.Click Exit Edit Mode.

The CAPTCHA WebPart will appear as below:


Code Download:

31 comments:

Anonymous said...

I get this error when I try to add the solution to my instace of MOSS 2007:

stsadm -o addsolution -filename Captcha.wsp

The solution references a namespace that is not valid. Ensure that the solution'
s manifest.xml file references "http://schemas.microsoft.com/sharepoint/"

I followed your instructions verbatim and seem to have hit a brick wall... could you provide a zip of the source code?

Nanddeep Nachan said...

Hello Anonymous,

I have added the zip of source code on the post.

Thanks & Regards,
Nanddeep Nachan

Anonymous said...

Hey Nanddeep,

does the my own webpart need to have some code to stop processing if the validation fails? how do i find out in my custom webpart that the validation has failed?

Cheers,
Jas

Nanddeep Nachan said...

Hello Jas,
Yes, your webpart should have a code to stop processing, if the validation fails.
For this, you can make use of ASP .Net Validation Controls.

For e.g.,
1) Required Field Validation:
To use this, you can write below code inside CreateChildControls() method:
private RequiredFieldValidator rfv;
rfv = new RequiredFieldValidator();
rfv.ControlToValidate = "txtCaptchaNumber";
rfv.Display = ValidatorDisplay.Dynamic;
rfv.Text = "*";
Controls.Add(rfv);

2) Custom Validation
private CustomValidator captchaValidator;
captchaValidator = new CustomValidator();
captchaValidator.ServerValidate += new ServerValidateEventHandler(captchaValidator_ServerValidate);
captchaValidator.Display = ValidatorDisplay.Dynamic;
captchaValidator.Text = "Incorrect Code";
Controls.Add(captchaValidator);

void captchaValidator_ServerValidate(object source, ServerValidateEventArgs args)
{
// if Validtion succeeds
args.IsValid = true;

// if Validtion fails
args.IsValid = false;
}

Hope, this answers your question.

Anonymous said...

Does this work only for a blog? is there a way to make this work for a newform.aspx page?

Nanddeep Nachan said...

Yes. This a web part, you can even add it to newform.aspx page

sonaly said...

Hello

How I can use webpart in newform.aspx?

Sorry for asking this question, as I am new bie in programming as well as in MOSS.

Please advise

Sonaly

Nanddeep Nachan said...

Hello Disha,
1. Open the NewForm.aspx in SharePoint Designer.
2. Click Add Web Part.
3. Add CAPTCHA web part.

Please let me know, how it works.

Regards,
Nanddeep

DC said...

I followed you detailed documentation and everything looks great, but I wasn't able to save my changed when I went to modify shared web part to change the chrome type to none or even the name

Anonymous said...

Hi Nanddeep,

I installed your web part into my development server and production server. It works great on development server. However, I got an error on my production server. I can't see the image at all. When I broswer to the url: http://mywebsite.com/_layouts/ACME/captcha.ashx

I see this error. Do you have an idea?

---------------------------
Object reference not set to an instance of an object. at captcha.ProcessRequest(HttpContext context)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

Troubleshoot issues with Windows SharePoint Services.

Henry Cheung

Nanddeep Nachan said...

Hello Henry,
Make sure you have allowed File type - .ashx on to production.

Regards,
Nanddeep

Unknown said...

Hi Nanddeep,

Do you think .Net Ajax need to be install in my SharePoint servers?

I found that all the working servers have .Net Ajax installed.

The only one that doesnt' work have no Ajax.

Thanks
Henry

Anonymous said...

Hi Nanddeep,

Great post, evrything works fine, except the session ["CAPTCHA"] never gets set. Any possible cause? I've set the sessionstate in web.config.

Regards,

Nanhui

Anonymous said...

hi..

ur captcha is really wrkg good....
but here i face a problem...
i create a webform then add the captcha there...its appear..and still can detect correct r incorrect captcha..the prob is, my web form have to savedata in sharepoint list, when i insert correct captcha n detail in form...the data is not insert in list...

how to solve?

Anonymous said...

I'm no coder, so I opted to use the wsp package and other files you provided in the zip. I followed the steps you specified in the CAPTCHA word document:

I couldn't find any captcha.ashx in the zip, so I copied the code and created the file myself. I created and put it into the 12\TEMPLATE\LAYOUTS\ACME directory.

I unblocked .ashx from the blocked file list in SharePoint Central Administration for both the application and the Central Administration sites.

I managed to install the wsp feature and activate it just fine in the blog site. I did the IISRESET.

However, for some odd reason that I cannot fathom, the CAPTCHA webpart never appears in the list of webparts when I try to add a webpart. I don't get any error either. Can you help me in this?

Anonymous said...

Oh, and additionally when I try to navigate to the http:///_layouts/ACME/captcha.ashx URL I get a SharePoint Error page. Is this normal?

Nanddeep Nachan said...

Hello Mikael,
Please try below steps:
1. Go to Site Settings.
2. Click Web Parts under Galleries.
3. In Web Part Gallery, Click New.
4. Select check-box next to CAPTCHA.
5. Click Populate Gallery button.

Now, you should see CAPTCHA web part in available web parts.

Let me know, if this helps you.

Nanddeep Nachan said...

Hello again Mikael,
No, that's not a normal behavior. Try navigating to page as:
http:///_layouts/ACME/captcha.ashx

for eg:
http://localhost:1589/_layouts/ACME/captcha.ashx

Anonymous said...

Thanks for your quick answer. Yes, I had forgotten to populate the gallery. I now can add the webpart just fine.

However, now it doesn't show the image despite refreshing the screen. If I understood the code correctly, it has to do with the .ashx file.

http://localhost/_layouts/ACME/captcha.ashx

Gets me an SharePoint unknown error. The eventlog mentions something about alternate access mappings and such.

http:///_layouts/ACME/captcha.ashx still gets me the Unknown Error, but with no Eventlog messages.

I checked http://:

ade
adp
app
asa
asmx
asp
bas
bat
cdx

What next?

Anonymous said...

Nanddeep, I've partly figured why captcha.ashx crashes in my testing environment.

I want to make the Captcha work in WSS 3.0. In the two environments with WSS 3.0 that I tested the captcha.ashx alone with, I encountered the SharePoint error. However, in our SharePoint 2007 environment, the captcha.ashx works just fine and displays an captcha image.

Can you tell what is the underlying problem, and perhaps compile a version for WSS 3.0 or explain if and how I can get it to work?

The Captcha webpart itself seems to work just as fine in WSS 3.0, so I suspect there's only a little that needs to be done for captcha.ashx to work properly.

Btw, what happens when the captcha verification fails? Will the user be notified about it?

Mikael

Nanddeep Nachan said...

Hello Mikael,
I need to re-check the code for WSS 3.0, as while writing I targeted it for MOSS.
User will not be able to submit the form, if the captcha verification fails.

Anonymous said...

Ok, please check the code. :)

Will the users be notified about the failed captcha verification? Or is there any other way I can specify what happens when they can't submit the form?

It needs to be explicit that the captcha verification failed.

Nanddeep Nachan said...

Hello Mikael,

I verified that CAPTCHA is working fine in WSS and MOSS both at my side.
The user will be notified with a error message of CAPTCHA verification fail.

Anonymous said...

Hello Nanddeep,

This is odd, I can't understand why the ashx file displays an error for me in my WSS 3.0 environments but not in the SharePoint 2007 standard environment.

Can you please email your ashx file to me? Do not publish this comment to avoid disclosing my email address. You can email it to mikael.henriksson@synocus.com.

Mikael

Mrugank said...

Hello Nanddeep

I have have used your code for CAPTCHA and works fine in Development but i am having one query before deploying on Live Server which is as follows

I am having server farm where i need to deploy this webpart on the site. will this webpart work on site which is deployed were there is server farm environment?

I have asked you the question as i am consern about sessionstate as it is used in webpart and i think it we require SQLServer Session for it.

So do i need to make any configuration for it in web.config to make it run in farm ?

Unknown said...

Hello Nanddeep,
I created the solution file, change a little code, then I followed the steps you specified in the CAPTCHA word document.I unblocked .ashx from the blocked file list in SharePoint Central Administration for both the application and the Central Administration sites.
I managed to install the wsp feature and activate it just fine in the blog site. I did the IISRESET.
However, webpart doesn't appear in Populate Gallery Web part list, and i can't add it. In what can be mistake?
Dasha.

Zhenya said...

Hello Nanddeep. I'm trying to adapt your code for sharepoint 2010 foundation. But CAPTCHA webpart never appears in the list of webparts when I try to add a webpart. I don't get any error either. Can you help me in this?

Unknown said...

i have try your Captcha Code and it's working...

kiat said...

Hey, how do you attach the validation to the button ?

Anonymous said...

Hi,
How to reload the text displayed when we click refresh button?
Pls share me the code to reload captcha image

Unknown said...

Hello,

I have used your captcha code .
Am using Sharepoint 2013. Am getting error "Application error when access /_layouts/15/captcha/captcha.ashx, Error=Object reference not set to an instance of an object." when i tried to check my ashx file.whereas simple Ashx work which print site url and name.

Let me know if i need to change anything,
I have removed ashx from blocked files.

World Clock