Tuesday, May 23, 2006

Easy PHP Captcha Tutorial

Easy PHP Captcha Tutorial



Today I'm going to show you how to quickly and easily write a captcha in PHP. You are free to use any of the code in your own work. All I ask is that you visit and consider joining the site that i wrote it for. [Relaywrite.com]


Here is a sample



The basic steps...



  • Create an image

  • Create random colors for background and text

  • Generate a random string of a random length

  • Generate a random angle for each character

  • Write characters to image

  • Put some random ellipses on the image some with the backround color and some with the text color

  • Save the string to a session variable for later verification

  • Output image to browser



Explanation...




Start PHP and start session session so that we can save our string into a session variable.



<?php
session_start();


Next we are going to generate a random number from 4 to 7 for our string length. After this we will use a for loop to assign a letter or number to each character in the string.

Note: I would generate a random number from 1 to 2 to decide if the character would be a letter or a number and then generate a random number from 48-57 for a number and assign the ASCII value of that to the character in the string and numbers from 65-90 for letters. But I started the numbers at 49 and excluded 79 to get rid of zero and O because they are too confusing.




$strlength = rand(4,7);

for($i=1;$i<=$strlength;$i++)
{
$textornumber = rand(1,3);
if($textornumber == 1)
{
$captchastr .= chr(rand(49,57));
}
if($textornumber == 2)
{
$captchastr .= chr(rand(65,78));
}
if($textornumber == 3)
{
$captchastr .= chr(rand(80,90));
}
}



Next we will generate some random RGB colors and initialize the image.




$randcolR = rand(100,230);
$randcolG = rand(100,230);
$randcolB = rand(100,230);

/initialize image $captcha is handle dimensions 200,50
$captcha = imageCreate(200,50);



Now we are going to set the colors for the image. The background colors will be the random RGB values that we just generated and we are going to make the text color a little lighter than the background color.

Note: the first color allocated is automatically used as the background color.



$backcolor = imageColorAllocate($captcha, $randcolR, $randcolG, $randcolB);

$txtcolor = imageColorAllocate($captcha, ($randcolR - 20), ($randcolG - 20), ($randcolB - 20));



And here is the heart of it all in this for loop. We set the for loop to iterate through each character. For each character we generate a random angle of rotation. We then use the php function imagettftext to place the characters in the image at the specified rotation at a random size from 14-20 pixels.


The syntax for the imagettftext function is:

array imagettftext ( resource image, float size, float angle, int x, int y, int color, string fontfile, string text )



for($i=1;$i<=$strlength;$i++)
{

$clockorcounter = rand(1,2);
if ($clockorcounter == 1)
{
$rotangle = rand(0,45);
}
if ($clockorcounter == 2)
{
$rotangle = rand(315,360);
}

//$i*25 spaces the characters 25 pixels apart
imagettftext($captcha,rand(14,20),$rotangle,($i*25),30,$txtcolor,"/arial.ttf",substr($captchastr,($i-1),1));
}



The hard part is over and we are almost done. We are going to throw some ellipses on top of the image. Some with the background color some with the text color. The intended effect is to break up the image to make it harder for an ocr program to read. There is a trade off here. If you use too many shapes it is difficult for a human to read. If you use to few then it is easy for an automated script to crack.



for($i=1; $i<=4;$i++)
{
imageellipse($captcha,rand(1,200),rand(1,50),rand(50,100),rand(12,25),$txtcolor);
}
for($i=1; $i<=4;$i++)
{
imageellipse($captcha,rand(1,200),rand(1,50),rand(50,100),rand(12,25),$backcolor);
}



All we have left is to output the image to the browser and save the string in a session variable so that we can verify it later.



//Send the headers (at last possible time)
header('Content-type: image/png');

//Output the image as a PNG
imagePNG($captcha);

//Delete the image from memory
imageDestroy($captcha);

$_SESSION[captchastr] = $captchastr;

?>


Some things you might want to try



  • Make the text color and the background colors closer together

  • Add more ellipses or increase the width of the ellipses

  • Try some other shapes

  • Make the string longer

  • Increase rotation angles

  • Visit my website[Relaywrite.com]

Using the code


You will need to have arial.ttf in the same directory. On your php valitation form use the session variable we created and compare it with the user input for validation. To add the captcha to your for use the following code:




<img src='captcha.php'>


Now here is all the code



<?php
session_start();
$strlength = rand(4,7);

for($i=1;$i<=$strlength;$i++)
{
$textornumber = rand(1,3);
if($textornumber == 1)
{
$captchastr .= chr(rand(49,57));
}
if($textornumber == 2)
{
$captchastr .= chr(rand(65,78));
}
if($textornumber == 3)
{
$captchastr .= chr(rand(80,90));
}
}
$randcolR = rand(100,230);
$randcolG = rand(100,230);
$randcolB = rand(100,230);

/initialize image $captcha is handle dimensions 200,50
$captcha = imageCreate(200,50);
$backcolor = imageColorAllocate($captcha, $randcolR, $randcolG, $randcolB);

$txtcolor = imageColorAllocate($captcha, ($randcolR - 20), ($randcolG - 20), ($randcolB - 20));
for($i=1;$i<=$strlength;$i++)
{

$clockorcounter = rand(1,2);
if ($clockorcounter == 1)
{
$rotangle = rand(0,45);
}
if ($clockorcounter == 2)
{
$rotangle = rand(315,360);
}

//$i*25 spaces the characters 25 pixels apart
imagettftext($captcha,rand(14,20),$rotangle,($i*25),30,$txtcolor,"/arial.ttf",substr($captchastr,($i-1),1));
}
for($i=1; $i<=4;$i++)
{
imageellipse($captcha,rand(1,200),rand(1,50),rand(50,100),rand(12,25),$txtcolor);
}
for($i=1; $i<=4;$i++)
{
imageellipse($captcha,rand(1,200),rand(1,50),rand(50,100),rand(12,25),$backcolor);
}
//Send the headers (at last possible time)
header('Content-type: image/png');

//Output the image as a PNG
imagePNG($captcha);

//Delete the image from memory
imageDestroy($captcha);

$_SESSION[captchastr] = $captchastr;

?>

-Josh

10 comments:

Loloy D said...

Your captcha engine looks interesting. Thanks. You may want to tweak it a little further by supporting a wider array of webserver. Some webservers are not intelligent or fault tolerant enough on determining when to start the HTTP body. Therefore, one may want to write an extra line
===
header('X-Captcha: By J Houle');
===
line after the line
===
header('Content-type: image/png');
===
to accomodate more webservers.

Also, some PHP versions/implementations may be unable to interpret (as in my case) undeclared variables. Adding the line
===
$captchastr = "";
===
should solve this problem.

And lastly, peeking at the HTTP stream, I noticed a problem on the line
===
$_SESSION[captchastr] = $captchastr;
===
so I corrected it to
===
$_SESSION["captchastr"] = $captchastr;
===

Now, your code behaves more universal and more fault-tolerant. Thanks.

Loloy D said...

I forgot to mention that the line
===
$captchastr = "";
===
should be at the top near and after the line
===
session_start();
===
but before the line
===
for($i=1;$i<=$strlength;$i++)
===

Prateek said...

this thing worked as a breeze,
just had 2 small hiccups though:

had to change
extension=php_gd.dll
or
extension=php_gd2.dll
in the php.ini

also, the line
/initialize image $captcha is handle dimensions 200,50
is actually a comment, the / slash might have been missed by u.

Tim said...

This is a great tutorial on the nitty gritty of image captcha creation. The code is a great learning tool!

Thanks!

- Tim

P.S. I have written an ASCII Captcha (requires no images or image libraries) - you can view an example of it here

Rastok said...

Ok je ovo samo kako provjeriti vrijednost sa slike i npr polja kojeg postavimo da se unosi

Unknown said...

how to check that the entered code is the same with that on the image?

Spas Cholakov said...

Hello, you have a misstake in the second code:
imagettftext($captcha,rand(14,20),$rotangle,($i*25),30,$txtcolor,"/arial.ttf",substr($
this line is not finished...

RichieRich said...

Hi Joshua,

Thanks heaps for your code.

For me the image displayed after I:

1. Uncommented extension=php_gd2.dll
in the php.ini and restarted the Apache Webserver

2. Removed the slash (/) just before 'arial.ttf': (the arial.ttf file being in the same folder)

imagettftext($captcha,rand(14,20),$rotangle,($i*25),30,$txtcolor,"arial.ttf"...

Thanks
Richard

Unknown said...

Ever heard of the pre tags to format your code? It's really hard to read.

Unknown said...

I want make my own captcha code you provide good information about Captcha Code it help me a lot.