MorkaLork Development

Interesting stuff I've picked up over the years...

PHP/Captcha

2009-06-05 18:07:31 | 245 views | php captcha security freetype libraries image

Introduction:


While creating different interactive pages on your website that includes the user to input data that will be sent to a server/database of some kind you should almost always (depending on what type of information it's about) try to make it as hard as possible for spam bots and intruders to bomb your forms. Sometimes you have to make sure that the incoming data is written by a human person and not a programmed computer of some sort. Here's where CAPTCHA's (Completely Automated Turing Test To Tell Computers and Humans Apart) come in handy, even though they seem to scare some people off because they, like (perhaps) you and I, finds it very annoying and offensive. Well, if you really have to use CAPTCHA's on your site, here's a fair tutorial just for you.


Prerequisites:


A basic knowledge and understanding of the different image-functions provided by GD Library that are being used in this tutorial! Check them up! (:


Requirements:


PHP 4+ with GD and FreeType libraries


Getting the business started:


The Form:


First of all we need to create a form that will be calling the php script it could look something like this:


<?php
session_start();

if($_SESSION['code'] == $_POST['code'] && !empty($_POST['name']))
{
$message = 'Thank you '.$_POST['name'];
unset($_SESSION['code']);
}
else
{
$message = 'Wrong or missing code or missing name, try again!';
}
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>
Test Page
</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body>
<h3>Test Page</h3>
<form id="testform" method="post" action="">
Your name:
<br/>
<input type="text" name="name" id="name" />

<br /><br />

<img src="captcha.php" alt="captcha" />
<br />
<input type="text" name="code" id="code" />

<br /><br />

<input type="submit" name="submit" id="submit" value="Send" />
</form>
<p>
<?php echo $message; ?>
</p>
</body>
</html>


First we start a session, and then we check if the form has already been submitted if it has it checks if the incoming code is the same as the code stored in the session variable (we are getting to that later) if it is we process the incoming data, if not we give an error message. In the HTML we call a PHP script instead of an image in our <img> because the PHP script itself will output an image.


The PHP:


We create a class for our CAPTCHA script and start by loading in a font for the image and defining our methods/functions and the constructor:


<?php
session_start();

class CaptchaImage
{
private $font = 'URL';

function GetCode()
{
}

function __construct($width, $height)
{
}
}

?>


Let's work with the constructor for a while. First we create a new image, and mix some colors for our program:


<?php
session_start();

class CaptchaImage
{
...

function __counstruct($width, $height)
{
$image = @imagecreate($width, $height);

$white = imagecolorallocate($image, 255, 255, 255);
$black = imagecolorallocate($image, 0, 0, 0);
$gray = imagecolorallocate($image, 51, 51, 51);
}
}

?>


Now we'll have a blank white image according to the sizes we send to our class constructor. Lets make some noise:


<?php
session_start();

class CaptchaImage
{
...

function __counstruct($width, $height)
{
...

$gray = imagecolorallocate($image, 51, 51, 51);

for($i = 0; $i < 750; $i++)
{
imagesetpixel($image, mt_rand(0,$width), mt_rand(0,$height), $gray);
}

for($i = 0; $i < 200; $i++)
{
imageline($image, mt_rand(0,$width), mt_rand(0,$height), mt_rand(0,$width), mt_rand(0,$height), $gray);
}
}
}

?>


If you would look at the image at this point it would probably look like this:

imagehttp://www.morkalork.com/admin/uploads/WimpySE/images/BILD1_CaptchaArtikel.png

The first loop randomly sets pixel dots all over the image, and the second draw random lines all over. Now, let's put our attention to the GetCode() function:


<?php
session_start();

class CaptchaImage
{
...

function GetCode()
{
$chars = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

$code = '';
for ($i = 0; $i <= 5; $i++)
{
$code .= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}

$_SESSION['code'] = $code;
return $code;
}

...

}

?>


This function first puts all possible/approved characters in a big string, then we use a loop and the substr() and mt_rand() functions to put a code string of 6 characters together from the string of possibles, and then we store the $code value in a session variable and return the code to the caller.

Now, when that's all cleared out, let's head back to the constructor to call for a code, setting a font size, and calculate the position of where to draw the code string on the image using the imagettfbbox() function:


<?php
session_start();

class CaptchaImage
{
...

function __construct($width, $height)
{
...

for($i = 0; $i < 200; $i++)
{
imageline($image, mt_rand(0,$width), mt_rand(0,$height), mt_rand(0,$width), mt_rand(0,$height), $black);
}

$code = $this->GetCode();
$fontsize = $height * 0.5;

$txtbox = imagettfbbox($fontsize, 0, $this->font, $code);
$x = ($width - $txtbox[4])/2;
$y = ($height - $txtbox[5])/2;
}
}

?>


We call the GetCode() function and stores the returning value in $code, then we set the font size to 50% of the $height value. After that we use the imagettfbbox() function to calculate the size of the box that will hold the text, this function returns an array with the coordinates of the four corners of the box. We use the top right corners' coordinates to calculate where to place the text in the image to get it centered.

Now the only thing left to do is to write out the text, and make some more noise:


<?php
session_start();

class CaptchaImage
{
...

function __construct($width, $height)
{
...

$x = ($width - $txtbox[4])/2;
$y = ($height - $txtbox[5])/2;

imagettftext($image, $fontsize, 0, $x, $y, $black, $this->font, $code);

for($i = 0; $i < 150; $i++)
{
imagesetpixel($image, mt_rand(0,$width), mt_rand(0,$height), $white);
}

for($i = 0; $i < 75; $i++)
{
imagesetpixel($image, mt_rand(0,$width), mt_rand(0,$height), $gray);
}
}
}

?>


That's almost all, we have to instantiate the class object and send it to the browser as well:


<?php
session_start();

class CaptchaImage
{
...

function __construct($width, $height)
{
...

for($i = 0; $i < 75; $i++)
{
imagesetpixel($image, mt_rand(0,$width), mt_rand(0,$height), $gray);
}

header('Content-Type: image/gif');
imagegif($image);
imagedestroy($image);
}
}

$width = 150;
$height = 75;

new CaptchaImage($width, $height);
?>


That simple and this is the result:

imagehttp://www.morkalork.com/admin/uploads/WimpySE/images/BILD2_CaptchaArtikel.png

Here is the whole code for you who don't have the time, or something, to copy/paste it from the tutorial:


<?php
session_start();

class CaptchaImage
{
private $font = 'URL';

function GetCode()
{
$chars = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

$code = '';
for ($i = 0; $i <= 5; $i++)
{
$code .= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}

$_SESSION['code'] = $code;

return $code;
}

function __construct($width, $height)
{
$image = @imagecreate($width, $height);

$white = imagecolorallocate($image, 255, 255, 255);
$black = imagecolorallocate($image, 0, 0, 0);
$gray = imagecolorallocate($image, 51, 51, 51);

for($i = 0; $i < 750; $i++)
{
imagesetpixel($image, mt_rand(0,$width), mt_rand(0,$height), $gray);
}

for($i = 0; $i < 200; $i++)
{
imageline($image, mt_rand(0,$width), mt_rand(0,$height), mt_rand(0,$width), mt_rand(0,$height), $gray);
}

$code = $this->GetCode();
$fontsize = $height * 0.50;

$txtbox = imagettfbbox($fontsize, 0, $this->font, $code);
$x = ($width - $txtbox[4])/2;
$y = ($height - $txtbox[5])/2;

imagettftext($image, $fontsize, 0, $x, $y, $black, $this->font, $code);

for($i = 0; $i < 150; $i++)
{
imagesetpixel($image, mt_rand(0,$width), mt_rand(0,$height), $white);
}

for($i = 0; $i < 75; $i++)
{
imagesetpixel($image, mt_rand(0,$width), mt_rand(0,$height), $gray);
}

header('Content-Type: image/gif');
imagegif($image);
imagedestroy($image);
}
}

$width = 150;
$height = 75;

$image = new CaptchaImage($width, $height);

?>


Here's a test page where you can try this out before writing it yourself, it's copied code line for code line from this tutorial: Link


Stuff to think of:


This is only an example of how you could build a program such as this and therefore I have omitted almost all of the security controls, bugfixes and stuff like that. Check it over and figure out what you can improve!

---

Use a clear font for the captcha that is readable even in smaller sizes, there's a lot of sites on the web where you can download free type fonts free of charge! Google it! Google it! Google it!

I used a font that's called Monofonto that can be downloaded here, for free.


Tutorial by,
WimpySE


Article comments

Feel free to comment this article using a facebook profile.

I'm using facebook accounts for identification since even akismet couldn't handle all the spam I receive every day.