Jump to content
xisto Community
rvalkass

A Simple Php Captcha - Image Validation

Recommended Posts

OK, I recently had the need to create a PHP CAPTCHA system for a friend, and I am sharing this as a tutorial with the good people here at Xisto. I am sure you have all seen a CAPTCHA before (although you may not have known what it was called). They are the little codes you often have to enter when you register with a site, to make sure you are a person and not an automated script. Some common examples look something like this:

 

Posted Image

 

My system doesn't really do anything as fancy, but I think that it is slightly more readable that some of those that get generated. Everyone wants to see the final product before they start, so here are a few examples of what this script generates:

 

Posted Image

 

OK, lets get started. Open up your favourite text/code editor (Kate, Notepad, Dreamweaver...) and you're ready.

 

First, we need to tell the browser what we are going to ouput at the end of this, seeing as it won't be standard HTML.

 

<?phpHeader ("(anti-spam-(anti-spam-(anti-spam-content-type:))) image/png");


Simple enough. We are creating an image. Its a PNG. Now we need some randomness in our process. I have used 2 random numbers here, one as a string to be encoded with MD5, and the other will be used to cut our 32 character MD5 output into a 8 character code.

 

$enc_num = rand(0, 9999); //This number will be encrypted$key_num = rand(0, 24); //This is used to choose which bit of our string to use in the image


Our first number is anywhere between 0 and 9999. It doesn't really matter what the range is, its just used to give MD5 something to encrypt. Our second number has to be specifically in the range of 0 to 24. Why? MD5 creates a 32 character string, of which we only need 8 characters. To add some extra randomness, this number chooses our starting character. It can't go above 24 (32-8) or we get less than 8 characters. So, to generate our string:

 

$hash_string = substr(md5($enc_num), $key_num, 8);


Now we can start on the actual image. Here is where you get some customisation. If you look at my example above, I have used 3 background images which I premade for my script to use. You can use as many or as few as you like, just add the file names to this array, and add that to your code so far. Make sure they are in the PNG format! My array looks like this (with the images in the same directory as the script):

 

$bgs = array("lipsum.png", "fibres.png", "rainbow.png");


Designing the backgrounds with an image editor allows you to make sure that the text will be readable on them. All of mine allow the text to be easily seen to a human, but not so easily to a computer. Now of course, we need to pick one of these images to use as the basis for our CAPTCHA. There is a useful function called array_rand which will select one item from an array at random. Bingo!

 

$background = array_rand($bgs, 1);


The GD library (available with all good hosts, such as Xisto) allows us to create images to output, and we will use this now. First, we create an image from a premade PNG background with this oh-so-inventively named function:

 

$img_handle = imagecreatefrompng($bgs[$background]);


Next, we set the text colour and size. I have used a white colour as it shows up well on all of my backgrounds. Try changing the values if white doesn't look good for you.

 

$text_colour = imagecolorallocate($img_handle, 255, 255, 255);$font_size = 5;


I've also done some fancy central alignment on the text, despite the fact all my images have the same dimensions ;) It allows for expansion, and you can design images to fit your site design.

 

$size_array = getimagesize($bgs[$background]);$img_w = $size_array[0];$img_h = $size_array[1];


That simply gets the width and height of the background in pixels. Now we do some maths to find where the top left of the text should go, compensating for the width of the text.

 

$horiz = round(($img_w/2)-((strlen($hash_string)*imagefontwidth(5))/2), 1);$vert = round(($img_h/2)-(imagefontheight($font_size)/2));


Lastly, we plug all this together. We add the text to the image, display it to the browser, then destroy the temporary file.

 

imagestring($img_handle, $font_size, $horiz, $vert, $hash_string, $text_colour);imagepng($img_handle);imagedestroy($img_handle);?>


If you have any problems then feel free to ask away and I'll do my best. In the future I may add the functionality for a PHP-generated background, random colours, custom fonts, and to apply some warping to the text. My aim is to still keep it readable though.

 

I carried out a (very un-scientific) test using my scanner's OCR software and that was unable to read the text. I am sure that if someone was determined and bothered enough then they would be able to break it, but it adds that little extra layer of protection to prevent people submitting things over and over, or computers being used to creat hundreds of messages, accounts...

 

I have also attached the completed source code and my images here too. Feel free to use them, and if you do it would be nice to see how you are using it. Also, feel free to offer improvements and suggestions.

Share this post


Link to post
Share on other sites

Very nicely done.

Excellent Tutorial style and presentation.

 

In spite of the testing which you have done, I would be concerned about the contrasting of the text against the backgrounds, so perhaps the text colour could be modified in this code snippet to blend the text into the chosen background a little bit more? There seems to be too much contrast of the text against the background and although your scanner didn't pick it up, other systems and methods may be able to deterine the text, so altering the colour might be required...?

 

$text_colour = imagecolorallocate($img_handle, 255, 255, 255);$font_size = 5;

Share this post


Link to post
Share on other sites

I've been thinking about that, and have tried a few things to combat it. Generating random colours was one option, but that can occasionally lead to one or two characters being a bit difficult to read. Another option was applying a filter across the image. I didn't know these existed until I started looking through the GD library's functions. Putting the following code just before the imagepng($img_handle); line creates a sort of jagged-blur effect across the image:

 

imagefilter($img_handle, IMG_FILTER_SMOOTH, 0.1);
It does sacrifice human readability a bit, generating images like this:

 

Posted Image

 

Adjusting the number 0.1 creates some different levels of the effect. I've tried running edge detection on the original images, and it proved quite reliable at picking the characters out. Random colours made life a bit more difficult, but this was the best way to beat edge detection, as now it picks edges up all over the image. With pixel maps for each character I think it would still be quite easy for someone to decipher it, but it's still an added layer.

 

I'll keep adjusting it and see what I can come up with.

Share this post


Link to post
Share on other sites

Very nicely put and extremely simple to follow. Maybe you could make a little form showing how to implent this actually into a web page, e.g. a contact us form. Or you could assign a session to this, and use md5 encryption on it to process this that way. I have quickly made an example of this:

Image.php:

<?phpsession_start();//CAPTCHA GENERATOR//Created by Rob Valkass 2007//Edited by QuickSilva//Feel free to distribute and use as you want//Just leave this notice and comments intact//Email: webmaster@rvalkass.co.uk//Web: rvalkass.co.uk///Set the header to say what sort of information we are giving the browserHeader ("(anti-spam-content-type:) image/png");//Generate 2 random numbers for use in our encryption$enc_num = rand(0, 9999); //This number will be encrypted$key_num = rand(0, 24); //This is used to choose which bit of our string to use in the image//Use these to get a random string of numbers and letters using MD5//We then cut the 32 char MD5 down to an 8 char string, starting from a random point$hash_string = substr(md5($enc_num), $key_num, 8);//MD5 the $hash_string variable again$hash_md5 = md5($hash_string);//Asign it to a session$_SESSION['captcha'] = $hash_md5;//Create an array of the images available to us as backgrounds$bgs = array("lipsum.png", "fibres.png", "rainbow.png");//Choose the background image using the handy array_rand function$background = array_rand($bgs, 1);//Now to start creating the all important image!//Set the background as our randomly selected image$img_handle = imagecreatefrompng($bgs[$background]);//Set our text colour to white$text_colour = imagecolorallocate($img_handle, 255, 255, 255);//Set the font size$font_size = 5;//Get the horizontal and vertical dimensions of the background image$size_array = getimagesize($bgs[$background]);$img_w = $size_array[0];$img_h = $size_array[1];//Work out the horizontal position$horiz = round(($img_w/2)-((strlen($hash_string)*imagefontwidth(5))/2), 1);//Work out the vertical position$vert = round(($img_h/2)-(imagefontheight($font_size)/2));//Put our text onto our imageimagestring($img_handle, $font_size, $horiz, $vert, $hash_string, $text_colour);//Output the imageimagepng($img_handle);//Destroy the image to free up resourcesimagedestroy($img_handle);?>
Form.php

 

<form method="post" action="process.php"><img src="image.php" border="0"><br /><input type="text" name="image"><br /><input type="submit" value="Submit"></form>
Process.php

 

<?phpsession_start(); //Start the session$session = $_SESSION['captcha']; //Define the session we set in image.php$image = $_POST['image']; //Define the post$image = md5($image); //MD5 encrypt the postif ($session == $image){ //if they have put the right text inecho "Correct!";}else{echo "Incorrect!";}?>
I have not tested this, so there maybe a few errors.

Share this post


Link to post
Share on other sites

I'm sure this will be great help to people who would like to implement a CAPTCHA image in their contact forum, thanks for the tutorial. Unlike others yours is very easy to read and understand. Once again thanks for creating it ;)

Share this post


Link to post
Share on other sites

Great tutorial!I can honestly say that I never took the time to think of the work involved in those things. Honestly, never cared to because they're annoying. However, seeing how they're made kinda makes me appreciate it.Again, great tutorial. Perhaps you'll have more coming in the future? We'll see, I guess.

Share this post


Link to post
Share on other sites

How do you implement this into a current form, or as a filter to the form pages? I really like this tutorial, since it's simple and easier to setup than other scripts on the internet.

Share this post


Link to post
Share on other sites

QuickSilva very handily wrote an example above, showing how you can implement this into a form. Basically you have to pass the text used on the image as a variable along with what the user enters. When they submit the form they are on, the script the form is passed to will check if the variable with the image text, and the one with the user's text match. If they do then they passed the CAPTCHA, if not, then tell them to go back and do it again.In the script, the variable $hash_string contains the text that appears on the image. If you pass that along with the form data as usual then in the script you have set up which deals with the form, one of the first things it should do is check that the user entered field and the $hash_string variable match (it's up to you if you want to make it case sensitive). QuickSilva has used sessions, which is another way to pass the variable along.If you want some help implementing this then I am happy to help you if you PM me with the details.

Share this post


Link to post
Share on other sites

Looks like a really good tutorial, but for someone like me who knows nothing about php and GD libraries, it doesn't do much for me. I need to add the captcha image validation to a form that I currently have on mywebsite that allows potential clients to sign up for my company's services. I have no idea how to get it done. I've tried many differnet tutorials, but I can never get it to work.What I need a step by step guide for complete idiots, which I am. I do have some knowledge of HTML,so there may be some hope for me yet.Any assistance you fine folks can provide will be greatly appreciated. I can be contacted atjeffny01@yahoo.comThanks in advance!Jeff Z.

Share this post


Link to post
Share on other sites

This is becoming really useful nowadays that bots are overloading blogs and phpbbs... I'm having probles myself, so I definitely will check this and see if I can use it.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...

Important Information

Terms of Use | Privacy Policy | Guidelines | We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.