Captcha - Spam preventing images

Tuesday May 30 2006 23:00

Captcha is the name for the images with random letters and some distortion that appears on each and every forum these days and even here on dublish. The purpose is to prevent the sites from spam. Here is an example in case you've no idea of what I'm talking about:

The idea with captcha is that one of the few things where human beats the computer is when it comes to detecting patterns. It is very hard for a computer to "read" skewed and distorted letters but hopefully not so hard for humans. So when you are asked to enter the letters you see in the pictures and you do it right the site can be pretty sure that you are a human.

Now I'm going to show you how to do this random picture generator in PHP. I assume that you have some basic PHP skillz before doing this. Before you start you need the GD library that allows you to create graphics in php. From php version 4.3 there is a bundled version of GD that works fine but if you have an earlier version you might need to download GD from http://www.boutell.com/gd/ and install it (follow the installation intructions at http://se2.php.net/manual/en/ref.image.php ).

I think I'll start by simply showing you the code for generating a image like the one above and then I will explain the code. This could also be good for the lazy readers who are not so much into understanding. So here you go:

<?php
session_start();
$passlen = 6;
$height = 30;
$width = 200;
$possiblechars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
$code = "";

// generate random letters
for($i = 0; $i<$passlen; $i++){
   $code = $code .
substr($possiblechars,rand(0,strlen($possiblechars)-1),1);
}

// lowercase > hash > store in session-variable
$_SESSION['code'] = md5(strtolower($code));

$image = imageCreateTrueColor($width, $height);

// make a gradient
for($i=0; $i<$width; $i++){
   $color = imagecolorallocate($image, $i*180/$width+75,
$i*180/$width+75, $i*180/$width+75);
   imageline($image, $i, 0, $i, $height, $color);
}

// print out the letters
for($i=0; $i<$passlen; $i++){
   $r = rand($i*13,$i*15);
   $textcolor = imageColorAllocate($image, $r, $r, $r);
   $font = rand(3,5);
   imagestring($image, $font, $width/$passlen*$i+rand(4,7),
$height/2-rand(imagefontheight($font)/2, imagefontheight($font)),
substr($code,$i,1), $textcolor);
}

// draw border
$bordercolor = imagecolorallocate($image, 80, 80, 80);
imagerectangle($image,0,0,$width-1,$height-1,$bordercolor);

// fix headers to avoid caching
header("Expires: Wed, 1 Jan 1997 00:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");

header('Content-type: image/gif'); // set content-type
imageGIF($image); // output the image
imageDestroy($image); // destroy image to free memory
?>

The concept here is to generate a random code, hash it and store it's hash value in a session variable. A hash value is a number generated from another value (or string) that seems to be totally random but if you hash the same thing again it hashes to the same value. To go backward though should be very hard, if you have the hash-value you are not supposed to be able to determine where it came from. When the user enters the code in your form and submits the form you can hash the code the user entered and compare it with the one that is saved in the session variable.

First part of the code declares some variables we will be using and runs session_start() so that we'll be able to use session variables.

The generation of the code is made by a for loop that runs as many times as the number of letters we want in our code. Each time it adds a substring from a random position of the string of possible chars.

When we have the code we store it in the a session variable called "code". Before we store it we make it lowercase because we don't want the case to matter, when we check if it matches with the code the user enters we will make that code lower case too. We also hash it with a hash algorithm called md5 that is built into php.

After this the graphical part begins. First we create the image so that we will have something to draw on. Then we want some background for the image and for our purpose a gradient is nice. If we had black letters on a white background it would be easier to distinguish the letters, the gradient will hopefully make it a little bit harder. We make the horizontal gradient by drawing lines from the top to the bottom for each pixel along the x-axis. The color of the lines get higher and higher RGB values the longer to the right we come. Exactly how the valus are calculated is not so interesting, I didn't want it to start with totally black so I added 75 and 180 is just some great distance from 255. Play around a bit with this code to make your own cool gradient.

Of course you are not limited to just grayscale, you can use whichever colors you would like. My thought here though was that I wanted the gradient to sometime be of the same color as some of the letters to make it hard for a computer to see the difference between the letters and the background.

After this it's time to start printing the letters. We want to make some special modifications to each letter so we loop through the code string. The letter needs a color, since the background goes from gray to white I thought it would be good if the colors of the letters went from black to gray. Then atleast the right letters would sometime have the same color as the color of the left background. We also want the color to be a little bit random so that the one who programs the bot can't just check what color to look for manually the first time and then let the bot always look for that color. When we have a color we also choose a random font. The imagestring function that we will use to output the letters uses built-in fonts that are named 1-5. Number 1 and 2 are to small to be good for our purpose so we randomize between 3 and 5. The random font makes it much harder for a computer to see the letters, if we hade just one font it could guess for a letter in that font and try it in different positions to see if it matches. With random fonts it will be a lot of more combinations to try. The imagestring line is very long but is actually not that interesting, it just prints out the letter with the color and font we choosed. The place for the output is a little bit random, both the x and the y position, but it's pretty limited to avoid letters to overlapse each other.

To finish up we draw a border around the image just to make it look better, this does nothing to make it harder for the bots.

After this we we set the headers of the image to make sure that it is not beeing cached. Caching is really really bad in this case because if the user writes the letter from a cached image it will not match with the code saved in the session which is of course bad bad bad.

Finally we set the content-type of the response that we are going to send to the browser to image/gif to make the browser present it like an image.

"imageGIF($image);" outputs our image in the GIF format. Other possible formats are JPG and PNG but then of course you also need to change the content-type. When we have outputted the image we destroy it to realease the memory.

Now you can save this as captcha.php or something like that. It doesn't matter that the file-extension is .php, the browser will read it like an image anyway because of the content-type. You can display it on a html page with the usual img-tag:

<img src="captcha.php">

If you really want the file-extension to be .gif you can put the file in a special directory and put a file called .htaccess in that directory. In .htaccess you write this line:

AddType application/x-httpd-php .gif

This makes your server read files with the .gif extension as php files in that directory. This only works on unix servers though.

The only thing left now is to make a page that uses the captcha picture and compares the users input to the code in the session-variable. Here is code for a very minimalistic example:

<html>
   <head>
      <title>Form protected with captcha</title>
   </head>
   <body>

<?php
   if ($_POST){
      session_start();
      if (md5(strtolower($_REQUEST['code'])) ==
$_SESSION['code']){
         echo "Correct code, you are human!";
      }else{
         echo "Wrong code, bots are not welcome!";
      }
   }else{
?>

      <form method="post">
         <p>
            <img src="captcha.php" />
         </p>
         <p>
            Code:
            <input type="text" name="code" maxlength="6" />
            <input type="submit" />
         </p>
      </form>

<? } ?>

   </body>
</html>

You can view this example here.

It's very simple, the only advanced thing is that it convert the code the user entered to lowercase, hashes it and then compares it with the code in the session variable. When you use this you will of course have more fields in your form where the real input for a comment or something is written but this was just a simple example on the critical parts.

A lot of improvements can be done to this, I just showed a simple example, now you can use the image-functions to make your own captcha. For example some distortion might make it harder for computers to read and you can rotate or skew the letters to make it even harder. Don't go too crazy though, in case you're not making a site for google there won't probably be anyone who cares that much about spamming your site that he builds a bot specialized to crack your captcha.

Something that is very important to think about is the usability. We want to prevent bots from using your site, not the people! Some people might have difficulties reading the text, be sure to use enough with contrast between the color of the letters and the background. Also avoid letters that can be hard to read, for example 0 and O looks very similar in some fonts, and l and 1 are also hard to distinguish. In my example there is only letters so that might hint the user that there are no 1's or 0's in the code but it might be even better to delete O and l from the list of possible chars too.

Anyway I hope this helped you making a better site by preventing it from evil spammers. Good Luck!

105 Comments

skanaar @ 2006-06-01 22:46:59

Interesting site with examples of good and bad captchas:

http://sam.zoy.org/pwntcha/

s @ 2006-06-22 21:01:47

s

Anonymous @ 2006-06-27 14:25:28

Anonymous @ 2006-07-07 01:04:47

Nice Article but seems to be caching wrong cant imagine its my browser its the same in both. Had to view image to get right details to enter.

Anonymous @ 2006-10-18 07:48:00

Anonymous @ 2006-11-24 17:57:48

PHPFan @ 2006-12-16 19:28:28

Great!!!! Very Useful.

Magio @ 2006-12-18 03:22:59

Really very useful article.. i am going to implement this on my blog

Hemid @ 2006-12-28 06:08:16

thank you for this article.. very useful one

Ganiow @ 2006-12-28 06:11:35

Useful one.. I applied this in my sites.. working like a charm

denday @ 2006-12-28 23:00:12

I wonder how to distinguish 0 (zero) and o (o letter).

ESPN @ 2007-01-25 21:38:30

Nice

Vijay @ 2007-01-31 18:08:22

Excellent one. Thanks for your pointers to create one easily.

Pozycjonowanie @ 2007-02-07 14:15:27

Excellent one. Thanks for your pointers to create one easily. Keep up the good work. Greetings

Anonymous @ 2007-02-20 16:47:16

Anonymous @ 2007-02-26 11:41:13

Anonymous @ 2007-03-04 04:13:55

Shopping @ 2007-03-11 21:47:17

very useful, thx

mookky @ 2007-04-02 09:51:25

Thank you very much for your kind knowledge.

Anon @ 2007-04-22 11:39:14

It was very usefull to "me". <3 E>

URL @ 2007-04-24 14:02:34

social work

Anonymous @ 2007-05-04 12:20:58

kjh @ 2007-05-13 22:33:01

jhkhjk

asd @ 2007-05-13 22:33:59

asf

jhkhjk @ 2007-05-14 19:18:48

jhkhjk

asf @ 2007-05-14 19:22:21

asd

kkf @ 2007-05-14 19:28:10

kfgkk

jhj @ 2007-05-14 19:30:50

fkfkfff

Anonymous @ 2007-07-07 22:42:49

test @ 2007-07-07 22:44:23

test

Abel @ 2007-11-19 21:32:26

Thank you very much!

bot @ 2007-12-14 05:18:54

captcha easily broken

sukomace @ 2008-01-28 09:03:44

<a href="http://wrdshqfp.com">ebbjcyyk</a> vaamvhto http://xwddjrlr.com uxyuahct lvisnxdq [URL=http://nzfvlifi.com]jodimnha[/URL]

Kyosho @ 2008-01-31 05:27:05

Thanks for sharing it. I like it, but if you can change the font,It might be better.

lzrgthck @ 2008-02-17 18:15:18

[URL=http://mnshpzgi.com]xgnvofir[/URL] qktfovui http://zivayhgd.com zaypgtvo bzvunwmb <a href="http://iiisbbsb.com">gkxtnxih</a>

generic propecia @ 2008-02-19 18:59:13

Marriage is the only adventure open to the cowardly.

stibo @ 2008-02-19 22:32:08

The man who has nothing to boast of but his illustrious ancestry is like the potato - the best part under ground.

generic tadalafil @ 2008-02-20 11:30:26

The meeting of two personalities is like the contact of two chemical substances: if there is any reaction, both are transformed.

cipro @ 2008-02-20 15:09:04

The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man.

tramadol online @ 2008-02-20 19:13:29

Self-importance is our greatest enemy. Think about it - what weakens us is feeling offended by the deeds and misdeeds of our fellowmen. Our self-importance requires that we spend most of our lives offended by someone.

benadryl @ 2008-02-20 22:52:32

Inside myself is a place where I live all alone and that's where you renew your springs that never dry up.

viagra funambulist r @ 2008-02-21 02:48:50

Vegetarianism is harmless enough, though it is apt to fill a man with wind and self-righteousness.

cheap valium @ 2008-02-21 06:04:28

You can go a long way with bad legs and a good head.

diflucan @ 2008-02-21 09:26:52

Good habits, which bring our lower passions and appetites under automatic control, leave our natures free to explore the larger experiences of life.

nexium online @ 2008-02-21 12:57:11

After the last of 16 mounting screws has been removed from an access cover, it will be discovered that the wrong access cover has been removed.

amoxicillin @ 2008-02-21 17:12:44

Doubt 'til thou canst doubt no more...doubt is thought and thought is life. Systems which end doubt are devices for drugging thought.

buy xenical docker h @ 2008-02-21 21:18:42

A man is too apt to forget that in this world he cannot have everything. A choice is all that is left him.

oukngwdl @ 2008-02-22 08:20:06

jtfjpbpz http://yosjpabh.com jisrydgb queokiey [URL=http://iwgcwmtz.com]emeutssx[/URL] <a href="http://lktjbpdy.com">qmmbinkr</a>

testosterone @ 2008-02-22 14:25:09

Forget injuries, never forget kindnesses.

meridia @ 2008-02-22 17:50:12

The average person thinks he isn't.

buy diazepam @ 2008-02-22 17:51:53

The most remarkable thing about my mother is that for thirty years she served the family nothing but leftovers. The original meal has never been found.

buy wellbutrin @ 2008-02-22 21:10:35

Paradise is exactly like where you are right now... only much, much better.

zestril @ 2008-02-23 02:37:36

Try to put your happiness before anyone else's, because you may never have done so in your entire life, if you really think about it, if you are really honest with yourself.

generic vicodin @ 2008-02-23 05:43:49

Try to put your happiness before anyone else's, because you may never have done so in your entire life, if you really think about it, if you are really honest with yourself.

choree @ 2008-02-23 05:48:28

Better by far you should forget and smile than you should remember and be sad.

order viagra online @ 2008-03-02 15:48:56

Reveal not every secret you have to a friend, for how can you tell but that friend may hereafter become an enemy. And bring not all mischief you are able to upon an enemy, for he may one day become your friend.

tenotomy @ 2008-03-02 23:20:42

Write a wise saying and your name will live forever.

electrodrill @ 2008-03-02 23:21:09

A doctor saves lives -- It's up to people to create lives that are worth saving.

ultram dolichocephal @ 2008-03-03 03:33:29

We should be eternally vigilant against attempts to check the expression of opinions that we loathe.

buy fioricet @ 2008-03-03 08:02:23

Equal opportunity means everyone will have a fair chance at being incompetent.

modifiable @ 2008-03-03 11:44:38

The only thing that saves us from the bureaucracy is inefficiency. An efficient bureaucracy is the greatest threat to liberty.

amoxycillin @ 2008-03-03 15:54:50

The movies are the only business where you can go out front and applaud yourself.

ultram online @ 2008-03-03 20:01:38

For the most part, fear is nothing but an illusion. When you share it with someone else, it tends to disappear.

generic paxil @ 2008-03-04 03:12:37

We thought, because we had power, we had wisdom.

neurontin @ 2008-03-04 06:50:42

It is curious that physical courage should be so common in the world and moral courage so rare.

generic plavix @ 2008-03-04 10:02:25

What if this weren't a hypothetical question?

buy hoodia @ 2008-03-04 13:34:55

What we become depends on what we read after all of the professors have finished with us. The greatest university of all is a collection of books.

bandlimitation @ 2008-03-04 17:29:59

Act as if it were impossible to fail.

tylenol @ 2008-03-04 21:08:11

The male is a domestic animal which, if treated with firmness, can be trained to do most things.

quqsvgbw @ 2008-03-05 06:38:35

<a href="http://vvrylymb.com">yorwcbxl</a> [URL=http://splsaxyu.com]afdbmbed[/URL] wezfujwx http://dcbnsyox.com kxnsexoo xxyfvlow

keflex @ 2008-03-05 13:40:36

The real hero is always a hero by mistake; he dreams of being an honest coward like everybody else.

ultram @ 2008-03-05 17:12:36

Most of the basic truths of life sound absurd at first hearing.

benadryl @ 2008-03-05 20:43:37

Hares can gambol over the body of a dead lion.

buspirone @ 2008-03-06 00:11:34

Over the years your bodies become walking autobiographies, telling friends and strangers alike of the minor and major stresses of your lives.

naprosyn @ 2008-03-06 07:37:11

If a dog jumps in your lap, it is because he is fond of you; but if a cat does the same thing, it is because your lap is warmer.

buspirone @ 2008-03-06 10:58:48

We thought, because we had power, we had wisdom.

purchase tramadol @ 2008-03-06 15:11:04

When we are unhurried and wise, we perceive that only great and worthy things have any permanent and absolute existence, that petty fears and petty pleasures are but the shadow of the reality.

generic wellbutrin @ 2008-03-06 19:00:19

Without the capacity to provide its own information, the mind drifts into randomness.

diflucan @ 2008-03-06 22:45:12

Normal is not something to aspire to, it's something to get away from.

Anonymous @ 2008-03-11 02:44:54

Anonymous @ 2008-03-11 02:45:17

zeka oyunlarý @ 2008-05-26 16:07:16

Thanks for this good blog text.

zeka oyunlarý @ 2008-05-26 16:07:47

Thanks for this good blog text.

komik oyunlar @ 2008-05-26 16:10:37

Thanks for this good blog text.

2 kiþilik oyun @ 2008-05-27 11:17:44

Thanks for this good blog text.

dövüþ @ 2008-05-27 11:22:14

Thanks for this good blog text.

hugo oyunlarý @ 2008-05-27 11:22:35

Thanks for this good blog text.

araba yarý&th @ 2008-05-28 13:04:18

Thanks for this good blog text.

bedava oyunlar @ 2008-05-28 13:04:38

Thanks for this good blog text.

kantýr @ 2008-05-28 13:04:56

Thanks for this good blog text.

msn nickleri @ 2008-05-28 13:05:23

Thanks for this good blog text.

beceri oyunlar&yacut @ 2008-05-28 15:10:51

Thanks for this good blog text.

en güzel oyunla @ 2008-05-28 15:11:19

Thanks for this good blog text.

güzel oyunlar @ 2008-05-28 15:12:05

Thanks for this good blog text.

oda oyunlarý @ 2008-05-28 15:12:18

Thanks for this good blog text.

Anonymous @ 2008-05-28 15:12:36

oyun @ 2008-05-28 15:12:56

Thanks for this good blog text.

oyun @ 2008-05-28 15:13:10

Thanks for this good blog text.

oyunlar @ 2008-05-28 15:13:26

Thanks for this good blog text.

var mýs&yacut @ 2008-05-28 15:13:42

Thanks for this good blog text.

varmýsý @ 2008-05-28 15:14:00

Thanks for this good blog text.

var mýs&yacut @ 2008-05-28 15:14:17

Thanks for this good blog text.

varmýsý @ 2008-05-28 15:14:32

Thanks for this good blog text.

at @ 2008-06-01 17:21:42

gghggfgbhfdýlgbvfdþgkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg

picture frame @ 2008-07-11 14:14:06

And there you have it. So start by reading the reviews mentioned below, and be sure to follow our rules as you shop for a digital photo frame.

Make a Comment

This article is hosted by dublish.com which is a free online publishing tool for those who have something to say but don't have their own blog. If you find this article abusive, report it to us.