|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Note: A new version of this control is available here. All further updates to the control are posted to the project's page on SourceForge.net.
IntroductionCAPTCHA is short for "completely automated public Turing test to tell computers and humans apart", and is the most popular technique used to prevent computer programs from sending automated requests to Web servers. These can be for meta-searching search engines, doing dictionary attacks in login pages, or sending spam using mail servers. You might have seen CAPTCHA images in the Google register page when you do availability check on too many usernames, or in Yahoo!, PayPal, or other big Web sites. SourcesThe first CAPTCHA image generator I used was written using the CAPTCHA Image article by BrainJar. After that, I read the MSDN HIP challenge article and made many changes to my code. The code used in this control is based on the MSDN HIP article, but some parts are not changed. How It Works
We will discuss some of them later in this article. The ControlThe main method in the control code is private void SetCaptcha()
{
// Set image
string s = RandomText.Generate();
// Encrypt
string ens = Encryptor.Encrypt(s, "srgerg$%^bg",
Convert.FromBase64String("srfjuoxp"));
// Save to session
Session["captcha"] = s.ToLower();
// Set URL
imgCaptcha.ImageUrl = "~/Captcha.ashx?w=305&h=92&c=" +
ens + "&bc=" + color;
}
This encrypts a random text using an encryption key which is hard-coded in this code. To prevent hard coding, you can store this information in the database and retrieve it when needed. This method also saves text to the session for comparison with user input. To make the control style match with the page style, there are two properties used:
The Style property sets the control style and the background color sets the background color for the generated image. Two event handlers handle the public delegate void CaptchaEventHandler();
When the user submits the form, protected void btnSubmit_Click(object s, EventArgs e)
{
if (Session["captcha"] != null && txtCaptcha.Text.ToLower() ==
Session["captcha"].ToString())
{
if (success != null)
{
success();
}
}
else
{
txtCaptcha.Text = "";
SetCaptcha();
if (failure != null)
{
failure();
}
}
}
RNG ClassThe public static class RNG
{
private static byte[] randb = new byte[4];
private static RNGCryptoServiceProvider rand
= new RNGCryptoServiceProvider();
public static int Next()
{
rand.GetBytes(randb);
int value = BitConverter.ToInt32(randb, 0);
if (value < 0) value = -value;
return value;
}
public static int Next(int max)
{
// ...
}
public static int Next(int min, int max)
{
// ...
}
}
RandomText ClassTo create a cryptographically-strong random text, we use the public static class RandomText
{
public static string Generate()
{
// Generate random text
string s = "";
char[] chars = "abcdefghijklmnopqrstuvw".ToCharArray() +
"xyzABCDEFGHIJKLMNOPQRSTUV".ToCharArray() +
"WXYZ0123456789".ToCharArray();
int index;
int lenght = RNG.Next(4, 6);
for (int i = 0; i < lenght; i++)
{
index = RNG.Next(chars.Length - 1);
s += chars[index].ToString();
}
return s;
}
}
CaptchaImage ClassThis is the heart of our control. It gets the image text, dimensions, and background color, and generates the image. The main method is private void GenerateImage()
{
// Create a new 32-bit bitmap image.
Bitmap bitmap = new Bitmap(this.width, this.height,
PixelFormat.Format32bppArgb);
// Create a graphics object for drawing.
Graphics g = Graphics.FromImage(bitmap);
Rectangle rect = new Rectangle(0, 0, this.width, this.height);
g.SmoothingMode = SmoothingMode.AntiAlias;
// Fill background
using (SolidBrush b = new SolidBrush(bc))
{
g.FillRectangle(b, rect);
}
First, we declare Now, we need to set the // Set up the text font.
int emSize = (int)(this.width * 2 / text.Length);
FontFamily family = fonts[RNG.Next(fonts.Length - 1)];
Font font = new Font(family, emSize);
// Adjust the font size until
// the text fits within the image.
SizeF measured = new SizeF(0, 0);
SizeF workingSize = new SizeF(this.width, this.height);
while (emSize > 2 &&
(measured = g.MeasureString(text, font)).Width
> workingSize.Width || measured.Height
> workingSize.Height)
{
font.Dispose();
font = new Font(family, emSize -= 2);
}
We calculate a size for the The next step would be adding the text. It is done using a GraphicsPath path = new GraphicsPath();
path.AddString(this.text, font.FontFamily,
(int)font.Style, font.Size, rect, format);
The most important part is colorizing and distorting the text. We set the text color using RGB codes, each one using a random value between 0 and 255. A random color is then generated successfully. Now, we must check if the color is visible within the background color. It's done by calculating the difference between the text color R channel and the background color R channel. If it is less than 20, we regenerate the R channel value. // Set font color to a color that is visible within background color
int bcR = Convert.ToInt32(bc.R);
int red = random.Next(256), green = random.Next(256), blue =
random.Next(256);
// This prevents font color from being near the bg color
while (red >= bcR && red - 20 < bcR ||
red < bcR && red + 20 > bcR)
{
red = random.Next(0, 255);
}
SolidBrush sBrush = new SolidBrush(Color.FromArgb(red, green, blue));
g.FillPath(sBrush, path);
Lastly, we distort the image by changing the pixel colors. For each pixel, we select a random picture from the original picture (the // Iterate over every pixel
double distort = random.Next(5, 20) * (random.Next(10) == 1 ? 1 : -1);
// Copy the image so that we're always using the original for
// source color
using (Bitmap copy = (Bitmap)bitmap.Clone())
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
// Adds a simple wave
int newX =
(int)(x + (distort * Math.Sin(Math.PI * y / 84.0)));
int newY =
(int)(y + (distort * Math.Cos(Math.PI * x / 44.0)));
if (newX < 0 || newX >= width)
newX = 0;
if (newY < 0 || newY >= height)
newY = 0;
bitmap.SetPixel(x, y,
copy.GetPixel(newX, newY));
}
}
}
Captcha.ashxThis HTTP handler gets the information needed to create a public class Captcha : IHttpHandler
{
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "image/jpeg";
context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
context.Response.BufferOutput = false;
// Get text
string s = "No Text";
if (context.Request.QueryString["c"] != null &&
context.Request.QueryString["c"] != "")
{
string enc = context.Request.QueryString["c"].ToString();
// space was replaced with + to prevent error
enc = enc.Replace(" ", "+");
try
{
s = Encryptor.Decrypt(enc, "srgerg$%^bg",
Convert.FromBase64String("srfjuoxp"));
}
catch { }
}
// Get dimensions
int w = 120;
int h = 50;
// Width
if (context.Request.QueryString["w"] != null &&
context.Request.QueryString["w"] != "")
{
try
{
w = Convert.ToInt32(context.Request.QueryString["w"]);
}
catch { }
}
// Height
if (context.Request.QueryString["h"] != null &&
context.Request.QueryString["h"] != "")
{
try
{
h = Convert.ToInt32(context.Request.QueryString["h"]);
}
catch { }
}
// Color
Color Bc = Color.White;
if (context.Request.QueryString["bc"] != null &&
context.Request.QueryString["bc"] != "")
{
try
{
string bc = context.Request.QueryString["bc"].
ToString().Insert(0, "#");
Bc = ColorTranslator.FromHtml(bc);
}
catch { }
}
// Generate image
CaptchaImage ci = new CaptchaImage(s, Bc, w, h);
// Return
ci.Image.Save(context.Response.OutputStream, ImageFormat.Jpeg);
// Dispose
ci.Dispose();
}
public bool IsReusable
{
get
{
return true;
}
}
}
There are only two points to be noted about this class:
SummaryWhen the control loads, it executes the You may see an example of using this control in the source code. | ||||||||||||||||||||