Posts Tagged ‘gd’

Creating Captcha Images with PHP and the GD Library

Friday, February 1st, 2008

A long time ago I wrote some code to create captcha images using PHP. The goal was to create similar or exact representations of the captcha’s used on Yahoo’s Overture. I really don’t have an answer why I chose their style except that it’s possible I liked the way they looked. I am sharing this code for creating the images but it’s up to you to create some logic in applying their use with HTML forms (I’ll give some hints at the end of this post).

This is the code to create the image. It returns an image resource identifier. Note the use of a custom TrueType font — you’ll need to change that line to the path of a font on your system.

<?php
// generate captcha image - returns image handle
function captcha_image($code_string, $img_width=150, $img_height=40) {
  // seed srand
  srand((double)microtime()*1000000);

  // create image
  $im = @imagecreate($img_width, $img_height) or die("Cannot Initialize new GD image stream");

  // security code
  $security_code = $code_string;

  // define font
  $font = "/usr/fonts/ttf/Georgia.ttf";

  // create some colors
  $black = imagecolorallocate($im, 0, 0, 0);
  $white = imagecolorallocate($im, 255, 255, 255);
  $grey = imagecolorallocate($im, 128, 128, 128);

  // randomness, we need lots of randomness :)
  // background color -> 1=black, 2=white, 3=grey (more colors can be added)
  // lines -> black bg (1=white or 2=grey), white bg (1=black or 2=grey), grey bg (black only)
  $randval = rand(1, 3);
  if ($randval == 1) {
    $bgcolor = $black;
    $fontcolor = $white;
    $linecolor = ((rand(0, 1) == 0) ? $black : $white);
  } elseif ($randval == 2) {
    $bgcolor = $white;
    $fontcolor = $black;
    $linecolor = ((rand(0, 1) == 0) ? $black : $white);
  } else {
    $bgcolor = $grey;
    $fontcolor = $black;
    $linecolor = ((rand(0, 1) == 0) ? $black : $grey);
  }

  // line positioning and increment
  $x_start = rand(0, 10);
  $x_size = rand(5, 10);
  $y_start = rand(0, 10);
  $y_size = rand(5, 10);

  // fill with background color
  imagefill($im, 0, 0, $bgcolor);

  // initial x position
  $font_x = 10;

  // write text
  for ($i = 0; $i < strlen($security_code); $i++) {
    // font size -> 20 to 35
    $font_size = rand(25, 35);
    // font angle -> -20 to +20
    $font_angle = rand(0, 20);
    if ($font_angle != 0) { if (rand(0, 1) == 0) { $font_angle = -$fone_angle; } }
    // font y position -> if font_size <= 27 then 30 to 35, if font_size > 27 then 30 to 35
    if ($font_size <= 27) { $font_y = rand(25, 30); } else { $font_y = rand(30, 35); }
    // write the text
    imagettftext($im, $font_size, $font_angle, $font_x, $font_y, $fontcolor, $font, $security_code{$i});
    // one more time to make it bolder
    imagettftext($im, $font_size, $font_angle, $font_x+1, $font_y+1, $fontcolor, $font, $security_code{$i});
    // next font x position
    $font_x += ($font_size + 5);
  }

  // draw horizontal lines
  for ($y = $y_start; $y < $img_height; $y += $y_size) {
    imageline($im, 0, $y, $img_width, $y, $linecolor);
  }
  // draw vertical lines
  for ($x = $x_start; $x < $img_width; $x += $x_size) {
    imageline($im, $x, 0, $x, $img_height, $linecolor);
  }

  // return captcha image handle
  return $im;
}
?>

We need a method for generating random four character strings when the captcha is created and displayed to the user. This function will do the trick.

<?php
function secret_key($length=4) {
  $salt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
  srand((double)microtime()*1000000);
  $i = 0;
  $skey = "";
  while ($i < $length) {
    $num = rand() % strlen($salt);
    $tmp = substr($salt, $num, 1);
    $skey .= $tmp;
    $i++;
  }
  return $skey;
}
?>

With everything in place, we can generate the secret key, create the captcha, and finally display it to the user using this code.

<?php
// set headers
header("Content-type: image/png");
header("Cache-Control: no-cache");
header("Pragma: no-cache");

// generate secret
$skey = secret_key();

// create captcha and output to browser as PNG image
$im = captcha_image($skey);
@imagepng($im);
@imagedestroy($im);
?>

Here is an example of the code in action. The style looks very similar to the images used on Yahoo’s Overture.

Captcha Demo ยป Captcha

Now you know how to create a captcha, so what about verifying the input against the captcha value? This can be accomplished a variety of ways and everyone tends to have their preference.

  • One method is to save the secret key in a session variable. As the image is created, store the key in a session variable and once the form is submitted, check the user’s value against the one stored in the session. If they match, proceed but if they fail, return an error and don’t process the form data.
  • If you don’t want to use sessions, you could try using temp files. Store the key in a temp file and pass some value as a query string identifying to the script that the key is in that file. Read in the key and MD5 or SHA1 crypt the key and save it in a hidden form field. When the form is submitted, compare the hashed key against the user input (which you will also hash). Process the form data if the keys match.

You can download the provided source file. Use the PHP file as an image source in your HTML IMG tag.

<img src="http://www.yourdomain.com/captcha.php">

Don’t forget to edit the path to the TrueType font you want to use in the captcha_image function. Failure to do so will lead to missing image characters.

Source Files: captcha.zip