|
|
|
|
![]() ![]() |
Feb 3 2007, 03:06 PM
Post
#1
|
|
|
apt-get moo ![]() Group: [MODERATOR] Posts: 2,254 Joined: 28-May 05 From: Devon, England Member No.: 7,593 ![]() myCENT:95.71 |
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 Trap17. 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:
![]() 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: ![]() 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. CODE <?php Header ("(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. 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: CODE $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): CODE $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! CODE $background = array_rand($bgs, 1); The GD library (available with all good hosts, such as Trap17) 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: CODE $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. CODE $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 CODE $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. CODE $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. CODE 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. Demo: http://www.rvalkass.co.uk/CAPTCHA/
Attached File(s)
captcha.php ( 1.85k )
Number of downloads: 79
rainbow.png ( 16.86k )
Number of downloads: 81
lipsum.png ( 7.25k )
Number of downloads: 65
fibres.png ( 8.31k )
Number of downloads: 61 |
|
|
|
Feb 3 2007, 03:41 PM
Post
#2
|
|
|
A computer once beat me at chess, but it was no match for me at kick boxing. ![]() Group: [MODERATOR] Posts: 4,326 Joined: 24-July 05 From: Linix, DOS and Windows…the good, the bad and the ugly Member No.: 9,787 ![]() myCENT:97.30 |
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...? CODE $text_colour = imagecolorallocate($img_handle, 255, 255, 255);
$font_size = 5; |
|
|
|
Feb 3 2007, 04:41 PM
Post
#3
|
|
|
apt-get moo ![]() Group: [MODERATOR] Posts: 2,254 Joined: 28-May 05 From: Devon, England Member No.: 7,593 ![]() myCENT:95.71 |
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:
CODE imagefilter($img_handle, IMG_FILTER_SMOOTH, 0.1); It does sacrifice human readability a bit, generating images like this: ![]() 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. |
|
|
|
Feb 4 2007, 07:18 PM
Post
#4
|
|
|
Premium Member ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Group: Members Posts: 181 Joined: 15-January 07 From: Rotherham, UK Member No.: 37,245 |
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: CODE <?php session_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: www.rvalkass.co.uk //Set the header to say what sort of information we are giving the browser Header ("(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 image imagestring($img_handle, $font_size, $horiz, $vert, $hash_string, $text_colour); //Output the image imagepng($img_handle); //Destroy the image to free up resources imagedestroy($img_handle); ?> Form.php CODE <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 CODE <?php session_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 post if ($session == $image){ //if they have put the right text in echo "Correct!"; }else{ echo "Incorrect!"; } ?> I have not tested this, so there maybe a few errors. |
|
|
|
Feb 5 2007, 12:13 AM
Post
#5
|
|
|
Premium Member ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Group: Members Posts: 167 Joined: 31-January 07 From: New Zealand Member No.: 38,062 |
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
|
|
|
|
Feb 5 2007, 05:30 AM
Post
#6
|
|
|
Member [Level 1] ![]() ![]() ![]() ![]() Group: Members Posts: 50 Joined: 5-February 07 Member No.: 38,291 |
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. |
|
|
|
Feb 14 2007, 11:09 AM
Post
#7
|
|
|
Newbie [Level 3] ![]() ![]() ![]() Group: Members Posts: 48 Joined: 27-January 07 Member No.: 37,955 |
wow, i love this good security tutorial, never again spam activity come to my site next time...
|
|
|
|
Mar 15 2007, 07:59 PM
Post
#8
|
|
|
Newbie [Level 2] ![]() ![]() Group: Members Posts: 25 Joined: 15-March 07 Member No.: 40,108 |
nice work, this will save a lot of people forums getting spammed.
Thanks, |