TalkPHP
 
 
Account Login
Latest Articles
» The basic usage of PHPTAL, a XML/XHTML template library for PHP
» Vulnerable methods and the areas they are commonly trusted in.
» Simple way to protect a form from bot
» The Basics On: How Session Stealing Works
» How to keep your forms from double posting data
IRC Channel
IRC Speech Bubble Join the friendly bunch on IRC...
(#TalkPHP on Freenode)

...Also available via a web interface.

See this thread for information on the TalkPHP Free Hugs Initiative™. Subject to availability.
Associates
Associates
CSS Tutorials
Reply
 
LinkBack Thread Tools Search this Thread Display Modes
Old 12-20-2007, 06:03 PM   #1 (permalink)
The Acquainted
Upcoming Programmer 
 
CMellor's Avatar
 
Join Date: Sep 2007
Location: Leeds, UK
Posts: 141
Thanks: 6
CMellor is on a distinguished road
Default [Tutorial] CAPTCHA

If you are un-aware of what CAPTCHA is, please refer to this.

Click for an example!

This is a tutorial on how to make your own CAPTCHA image, and how to use it! Let's begin!

First open up a PHP file, save it to your server and call in captcha.php

We will be using a session variable to store our CAPTCHA code in for later use, so we need to make sure we can use sessions.

PHP Code:
session_start(); 
We're gonna store this code inside a function, though for this particular example, you don't need to, it is optional, but as I say, I will be storing it inside a function.

The function will be called captcha
PHP Code:
function captcha($length 6) { // Start of function 
The $length variable is defaulted to 6, though this can be any number you wish, though I recommend you keep it between 4 and 6.

Tip: if your going to use this with different forms and specify different lengths for each one, then do not specify a length, just keep it as $length

When using the CAPTCHA code, you can specify a length by adding a GET method to your URL to specify how many characters you want your CAPTCHA code to be, but we'll come back to that at the end of this tutorial. We need to check if one is set, otherwise it will be set to it's default number.

PHP Code:
$length = isset($_GET['chars']) ? $_GET['chars'] : 6
Now we need to generate a random string, which will be our random CAPTCHA code. I will do this my getting a complete random number between 0 and 999 and then encrypting it, this will be referred to as the hash.

PHP Code:
$hash md5(rand(0999)); 
Now I know what your thinking, when you encrypt a string, it turns it into a 32 character string, well indeed it does, which is why we trim it down to the length we specified earlier in our$length variable.

PHP Code:
$code substr($hash15$length); 
What this does is takes the random generated hash, starts at the 15th character and trims it down to $length, which in this case, is 6.

Now that we have our random code, we need to store it in a session, which is simple enough.

PHP Code:
$_SESSION["captcha"] = $code
Self explanatory I would think.

Okay! Now onto creating the image. Warning: you must have GD installed on your server.

We need to create the image size, so let's set a width and a height... this can be to any size you wish, but I recommend keeping it at around the size specified in this tutorial, it is a good size.

PHP Code:
$width 200;
$height 50
Now we create our image using the set variables above.

PHP Code:
$image imagecreate($width$height); 
Now just because we can, lets make the image colourful. We'll assign some random RGB (red, green, blue) colours so that on each load, it'll most likely be a different colour. We're not using HEX colours in this, as the function we're using doesn't support it (or at least not that I know of) and to make a colour using RGB, you need to set three numbers between 0 and 255 to get the colour you want. Because we need three numbers, we assign three variables with the same value, because if we set just one variable with the same value, it will always be the same colour.

PHP Code:
$rgb1 rand(0255);
$rgb2 rand(0255);
$rgb3 rand(0255); 
Still with me? Good...

Now we need to colour in our image we made earlier, we do this like so:

PHP Code:
$background imagecolorallocate($image$rgb1$rgb2$rgb3); 
This will make our image a different colour on each reload. Now let's do the same for the text, which is the CAPTCHA code. The only different here is that with the given random number generated, we minus 50 and it gives it more of a transparent look, which trust me, looks good. This is of course optional.

PHP Code:
$text imagecolorallocate($image, ($rgb1 50), ($rgb2 50), ($rgb3 50)); 
Now we have our set variables, we create the final image with our background colour.

PHP Code:
imagefill($image00$background); 
Now we are going to add the random CAPTCHA code to the image. A difference here is the separate characters will be set at an angle, just so that it isn't TOO clear to read, but also not difficult. Also we will use a function that allows you to specify your own font for the image. You'll need to either get a font from your computer, or download one from a website (DAFont.com is a good source for free fonts) and upload it to your server, preferably in the same folder as captcha.php.

To get different angles for the characters, we'll go through a loop and set our $angle variable to be different after each loop (The loop will loop through the amount of times you've specified in the $length variable, in this case, 6.

PHP Code:
for($i 1$i <= $length$i++) {
$counter rand(12);
        
if (
$counter == 1) {
    
$angle rand(045);
}
if (
$counter == 2) {
    
$angle rand(315360);
}
        
imagettftext($imagerand(1420), $angle, ($i 25), 30$text"templates/ChalkboardBold.ttf"substr($code, ($i 1), 1));

What this does, is simply... after a loop has happened, $counter is set to either 1 or 2, if it is 1 then $angle will equal a number between 0 and 45 and if it is 2, it will equal a number between 315 and 360.

Now I'm going to go through the parameters of the imagettftext function.
  • $image is the image you will be using, you specified it earlier
  • This is the size of the font
  • The set angle
  • The X co-ordinates
  • The Y co-ordinates
  • The text colour
  • The path to your font file
  • The text output
Okay so now you should have your image, with a background and font colour, as well as the actual CAPTCHA code on the image as well.

Optional: You could add a border to your image, in my opinion it makes it look better, but as it says, this step is optional. To do this we use a new function called imagerectangle

PHP Code:
imagerectangle($image00$width 1$height 1$text); 
The numbers in this function relate to the upper and lower co-ordinates of X and Y. If you minus 1 from the width and height, it will shrink the image by a pixel and show the rectangle behind it, which gives it the "border" effect.

This step is important, without it you can't view the image, because your working on a PHP file, and the browser will recognise it as a PHP file, and this is an image, so you need to make the browser recognise it as an image, and we do that by using the header function and changing the content type.

PHP Code:
header("Content-Type: image/png"); 
Now the page will act as if it is a PNG file. You could use image/jpg or image/gif if you wish, but for this we're using a PNG.

Now that we have everything done and all put together, we need to output the image using a special function called imagepng, like before, if you decide to use JPG or GIF, change the name of the function to represent your desired extention. (I.E. JPG = imagejpg)

[php]
imagepng($image);
[/php
Now that we have our image done and ready to use, let's free up the resource used and 'destroy' the image (don't worry, it will still show up)

PHP Code:
imagedestroy($image);
// End function 
Something I did after the function was this:

PHP Code:
!isset($_GET['chars']) ? captcha() : captcha($_GET['chars']); 
Basically is chars is not set, it will output the CAPTCHA with the defaulted length, otherwise, it will display it with the number specified by you.

Now save this file, and try it out, open up your browser and point to the file and fingers crossed that it works! Any problems and let us know!

Using the script in your site and verifying it

Ok so you have the script, now you want to use it to prevent attacks on your forms. First get the file with the form you will be using it on. Anywhere in the code, add another input box, this will be used to type in what you see in the CAPTCHA code.

html Code:
<input type="text" name="captcha_confirm" />
Above (or below) that, add your CAPTCHA image to it. As we made the CAPTCHA file into an image, we can use the img tag to display it.

html Code:
<img src="path/to/captcha.php" />
Now you should have your CAPTCHA code displayed, woo!

Verifying

This tutorial assumes that when you submit your form, it uses PHP to process the form to do... whatever it does. Now around the code your using, you need to check if the CAPTCHA matches what was typed in. I will use an example that I did.

PHP Code:
if($_POST['captcha_confirm'] == $_SESSION["captcha"]) {

    
// Update user's e-mail address
    
mysql_query(sprintf("UPDATE members, settings SET
        settings.hide_email = '%d',
        members.email = '%s'
    WHERE members.id AND settings.member_id = '%d'"
,
        (isset(
$_POST['hide_email']) ? 0),
        
$_POST['confirm_email'],
        
$_COOKIE['userID']
    )) or die(
mysql_error());
    
// Above is the code I have... your code will be whatever it was, just wrap the if statement around it
}
else {
    echo 
'Captcha did not match';

Now when you submit your form, and if the CAPTCHA is wrong, you should get an error instead of your code being processed.

So that's all their is to it, I hope this tutorial is helpful to you, if you have any problems, please just ask, we're here to help!

Full code
PHP Code:
<?php
session_start
();
//--------------------
// CAPTCHA Function
//--------------------
function captcha($length 6) {
    
$length = isset($_GET['chars']) ? $_GET['chars'] : 6;
    
    
// Let's generate a totally random string using md5
    
$hash md5(rand(0999)); 
    
// We don't need a 32 character long string so we trim it down to 5 
    
$code substr($hash15$length); 

    
// Set the session to store the security code
    
$_SESSION["captcha"] = $code;

    
// Set the image width and height
    
$width 200;
    
$height 50;

    
// Create the image resource 
    
$image imagecreate($width$height);
    
// Random RGB colours
    
$rgb1 rand(0255);
    
$rgb2 rand(0255);
    
$rgb3 rand(0255);
    
    
$background imagecolorallocate($image$rgb1$rgb2$rgb3);
    
$text imagecolorallocate($image, ($rgb1 50), ($rgb2 50), ($rgb3 50));

    
// Make the background colour
    
imagefill($image00$background); 

    
// Add randomly generated string in the image
    
for($i 1$i <= $length$i++) {
        
$counter rand(12);
        
        if (
$counter == 1) {
            
$angle rand(045);
        }
        if (
$counter == 2) {
            
$angle rand(315360);
        }
        
        
imagettftext($imagerand(1420), $angle, ($i 25), 30$text"templates/ChalkboardBold.ttf"substr($code, ($i 1), 1));
    }

    
// Add a border
    
imagerectangle($image00$width 1$height 1$text); 
 
    
// Tell the browser what kind of file is come in 
    
header("Content-Type: image/png"); 

    
// Output the newly created image in jpeg format 
    
imagepng($image);
   
    
// Free up resources
    
imagedestroy($image); 
}

!isset(
$_GET['chars']) ? captcha() : captcha($_GET['chars']);
?>
__________________
Not quite a n00b...
CMellor is offline  
Reply With Quote
The Following 6 Users Say Thank You to CMellor For This Useful Post:
Andrew (12-20-2007), Gurnk (12-21-2007), Rendair (12-20-2007), sketchMedia (12-21-2007), Village Idiot (12-21-2007), Wildhoney (12-20-2007)
Old 12-20-2007, 10:47 PM   #2 (permalink)
The Addict
Upcoming Programmer Top Contributor 
 
Rendair's Avatar
 
Join Date: Nov 2007
Location: UK
Posts: 319
Thanks: 18
Rendair is on a distinguished road
Default

Nice script i might have to use that for one of my projects
__________________
www.jooney.co.uk - the online portfolio
Send a message via MSN to Rendair
Rendair is offline  
Reply With Quote
Old 12-21-2007, 12:28 AM   #3 (permalink)
The Acquainted
Upcoming Programmer 
 
CMellor's Avatar
 
Join Date: Sep 2007
Location: Leeds, UK
Posts: 141
Thanks: 6
CMellor is on a distinguished road
Default

Awesome.

A lot more could easily be added to it. Previously I had a line going through the code, to make it a little less readable, but in some cases it was really unreadable, so I removed it. Also instead of a one colour background, you could use a ready made background. I personally just wanted to keep it simple and readable and I think I achieved that
__________________
Not quite a n00b...
CMellor is offline  
Reply With Quote
Old 12-21-2007, 01:50 AM   #4 (permalink)
The Contributor
Upcoming Programmer 
 
Gurnk's Avatar
 
Join Date: Oct 2007
Location: US
Posts: 66
Thanks: 19
Gurnk is on a distinguished road
Default

Very nice tutorial. :) Thanks, helped me a lot.
Send a message via MSN to Gurnk
Gurnk is offline  
Reply With Quote
Old 12-21-2007, 02:00 AM   #5 (permalink)
The Acquainted
Upcoming Programmer 
 
CMellor's Avatar
 
Join Date: Sep 2007
Location: Leeds, UK
Posts: 141
Thanks: 6
CMellor is on a distinguished road
Default

That's great!

In a way, it was the first tutorial I've wrote out. I think (and hope) I described it as best I can, in my way, which is a simple, easy to understand way. I'm considering doing another one, just not sure what on yet, my first initial thought is something Ajax'y

BTW, just out of curiosity, could this be considered for submission to *Insert tutorial listing website here*
__________________
Not quite a n00b...
CMellor is offline  
Reply With Quote
Old 12-21-2007, 11:05 AM   #6 (permalink)
Moderateur
RegEx Guru PHP Guru Top Contributor Advanced Programmer 
 
Salathe's Avatar
 
Join Date: Apr 2007
Posts: 1,393
Thanks: 5
Salathe is on a distinguished road
Default

It is a nice little tutorial but I can see potential problems with it -- things that you might want to improve somewhere down the line. But because you haven't asked for comments or constructive criticism, I won't go there.

As for your question about being submitted to tutorial aggregators, I don't see why they wouldn't accept. It's a useful guide!
Salathe is offline  
Reply With Quote
Old 12-22-2007, 08:33 PM   #7 (permalink)
The Acquainted
Upcoming Programmer 
 
CMellor's Avatar
 
Join Date: Sep 2007
Location: Leeds, UK
Posts: 141
Thanks: 6
CMellor is on a distinguished road
Default

Actually if anybody wants to give any C&C, please do so
__________________
Not quite a n00b...
CMellor is offline  
Reply With Quote
Old 12-28-2007, 08:13 PM   #8 (permalink)
The Contributor
 
nefus's Avatar
 
Join Date: Nov 2007
Location: Nashville, TN
Posts: 66
Thanks: 20
nefus is on a distinguished road
Default

I get a variety of interesting errors. I am a noob so please bare with me.

Warning: imagettftext() [function.imagettftext]: Invalid font filename in C:\Program Files\xampp\htdocs\x\user_process\capcha.php on line 45

AND

Warning: Cannot modify header information - headers already sent by (output started at C:\Program Files\xampp\htdocs\x\user_process\capcha.php:45) in C:\x\xampp\htdocs\x\user_process\capcha.php on line 52
‰PNG  ��� IHDR������2���#Ÿ���PLTEOb���IDATxœc 040`Œ2222C\g� Io����IENDB`‚
or This one

The image “http://x.dyndns.org/x/user_process/signup.php” cannot be displayed, because it contains errors.
nefus is offline  
Reply With Quote
Old 12-29-2007, 05:38 AM   #9 (permalink)
The Wanderer
 
deflated's Avatar
 
Join Date: Dec 2007
Location: 127.0.0.1
Posts: 19
Thanks: 7
deflated is on a distinguished road
Default

nefus, you get the warning "Cannot modify header information..." because imagettftext() has also caused a warning which has already been displayed. A HTTP response contains two parts: The header and the body. You can't mix them! That's what has happened here. Your websever (most likely Apache because of XAMPP) sent the header first. Then PHP caused a warning which threw an output. Because Apache does not store both parts in a local cache for performance reason it will send all the data to the client. Meaning when you output something Apache will end the header and will 'initialize' the body so that it isn't possible anymore to send headers. Ok, regarding your problem. It seems so that the font (in this case templates/ChalkboardBold.ttf does not exist). imagettftext() isn't able to find it that's because it threw the warning. Just try arial.ttf or any other font that's installed on your system. On production systems there shouldn't be any PHP warnings and errors shown at all! People could use the provided information and might be able to break into your system especially if it's a Windows-based server . I recommend you to use an error handler. If you're interested in I could publish one I've written some time ago.

Last edited by deflated : 07-18-2010 at 01:27 PM.
deflated is offline  
Reply With Quote
Old 12-30-2007, 03:53 AM   #10 (permalink)
The Acquainted
Upcoming Programmer 
 
CMellor's Avatar
 
Join Date: Sep 2007
Location: Leeds, UK
Posts: 141
Thanks: 6
CMellor is on a distinguished road
Default

Yes, as deflated has stated, the reason for your error was because your font file is invalid, you've used the one that's hardcoded into my script without realising you do not have that font on your system.

Grab a font file from your font folder in Windows, copy and paste it to your 'www' folder and link to it in the part of the script and this should solve your problem.
__________________
Not quite a n00b...
CMellor is offline  
Reply With Quote
Old 02-21-2008, 10:40 PM   #11 (permalink)
The Visitor
 
Join Date: Feb 2008
Posts: 2
Thanks: 0
qaiphyx is on a distinguished road
Default

I cant seem to get the image to dispay, I have everyone built just right, the .php file, arial.tff font, I changed where it pulls the ttf file from, but when I load the page, it only shows the code input but not the image. Whats going wrong?
qaiphyx is offline  
Reply With Quote
Old 02-22-2008, 01:30 AM   #12 (permalink)
The Visitor
 
Join Date: Feb 2008
Posts: 2
Thanks: 0
qaiphyx is on a distinguished road
Default

Here's what I have.

Quote:
<?php
session_start();
//--------------------
// CAPTCHA Function
//--------------------
function captcha($length = 6) {
$length = isset($_GET['chars']) ? $_GET['chars'] : 6;

// Let's generate a totally random string using md5
$hash = md5(rand(0, 999));
// We don't need a 32 character long string so we trim it down to 5
$code = substr($hash, 15, $length);

// Set the session to store the security code
$_SESSION["captcha"] = $code;

// Set the image width and height
$width = 200;
$height = 50;

// Create the image resource
$image = imagecreate($width, $height);
// Random RGB colours
$rgb1 = rand(0, 255);
$rgb2 = rand(0, 255);
$rgb3 = rand(0, 255);

$background = imagecolorallocate($image, $rgb1, $rgb2, $rgb3);
$text = imagecolorallocate($image, ($rgb1 - 50), ($rgb2 - 50), ($rgb3 - 50));

// Make the background colour
imagefill($image, 0, 0, $background);

// Add randomly generated string in the image
for($i = 1; $i <= $length; $i++) {
$counter = rand(1, 2);

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

imagettftext($image, rand(14, 20), $angle, ($i * 25), 30, $text, "arial.ttf", substr($code, ($i - 1), 1));
}

// Add a border
imagerectangle($image, 0, 0, $width - 1, $height - 1, $text);

// Tell the browser what kind of file is come in
header("Content-Type: image/png");

// Output the newly created image in jpeg format
imagepng($image);

// Free up resources
imagedestroy($image);
}

!isset($_GET['chars']) ? captcha() : captcha($_GET['chars']);
?>
And for the image display page.

Quote:
<input type="text" name="captcha_confirm" />

<img src="captcha.php" />
theyre all in the same folder, and arial.ttf is in there too.

what am I doing wrong it doesnt show the img at the img src. its just white.
qaiphyx is offline  
Reply With Quote
Old 02-22-2008, 02:10 AM   #13 (permalink)
Moderateur
RegEx Guru PHP Guru Top Contributor Advanced Programmer 
 
Salathe's Avatar
 
Join Date: Apr 2007
Posts: 1,393
Thanks: 5
Salathe is on a distinguished road
Default

@CMellor One thing that you might want to do is after a successful CAPTCHA request/response, clear the session's "captcha" value. Otherwise people can capture the session id and a successful CAPTCHA response, and use it over and over and over again (hmm sounds like something a bot would do). Also at the moment, there's nothing to stop people adding their own '?chars=0' to the end of the CAPTCHA image and then sending along an empty response.
Salathe is offline  
Reply With Quote
Old 06-01-2008, 08:33 PM   #14 (permalink)
The Contributor
 
Join Date: May 2008
Location: Denmark
Posts: 70
Thanks: 3
SpYkE112 is on a distinguished road
Default

I'm afraid theres a little security breach... As far as i see this piece of code:
Code:
$hash = md5(rand(0, 999));
Leaves us with 999 different hashes.. It takes a couple of milliseconds to calculate those, and voila a bot knows all the different combination and a dictionaty attack can begin... what i would do...

change it to something like:
Code:
$hash = md5(rand(0, 999*10^10000));
That should leave us with a whole lot more combinations, acutally 999 + 10002 zeros :)

But thats probaly the easy way... I would rather make a pattern with lower case, upper case and chars which should be approx 65 chars, then i would calculate a string with like 32 chars and hash it and then substr it.. Should make it even more bot secure ;)
SpYkE112 is offline  
Reply With Quote
Reply



Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On


All times are GMT. The time now is 06:13 AM.

 
     

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2013, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.1.0
Inactive Reminders By Icora Web Design