How to create captcha in PHP using GD library

Today I will tell you about very important thing – captcha protection. Usually this is very important to prevent automated sign-ups in your registration forms, comment spam in blogs and guestbooks, or another brute force attacks. I will give sample how to draw protection image using our hands.

 

Step 1. HTML

As usual, we start with the HTML.

This is our main page code.

index.html

HTML
<script src="js/jquery.min.js"></script>
<script src="js/md5.js"></script>

<script src="js/main.js"></script>
<link rel="stylesheet" href="templates/css/main.css" type="text/css" />

<div class="captcha_example">
    <h3><a href="#">Captcha generation using GD, using MD5 to encrypt security text</a></h3>
    <div>
        <div style="margin-bottom:10px;">
            <h4>Security image:</h4>

            <img src="captcha.php" class="form_captcha" />
            <div class="lines">Verification (Type what you see):</div>
            <input type="text" name="captcha" value="" class="captcha" />
            <button onclick="checkCaptcha()">Check it</button>
        </div>
        <p>
            Current sample draw security image using GD library, each time when page loads it show different symbol-digit combinations. This combination processing through MD5 encription and storing in cookies. So you will able to check cookies after (where it necessary). In my sample I checking it using javascript. Try it!
        </p>
    </div>
</div>

To display captcha (security image) we will just draw image with php-source: captcha.php. It will generate image for us. Generated text will processed via MD5 and stored in cookies to following recognition and checking.

Step 2. CSS

Here are used CSS styles.

templates/css/main.css

CSS
body{background:#eee;font-family:Verdana, Helvetica, Arial, sans-serif;margin:0;padding:0}
.captcha_example{background:#FFF;width:865px;font-size:80%;border:1px #000 solid;margin:0.5em 10% 0.5em;padding:1em 2em 2em;-moz-border-radius: 3px;-webkit-border-radius: 3px}
.lines{margin:10px 0}

Step 3. JS

Here are necessary JS files to our project.

js/main.js

JavaScript
function getCookie(c_name) {
    if (document.cookie.length>0) {
        c_start=document.cookie.indexOf(c_name + "=");
        if (c_start!=-1) {
            c_start=c_start + c_name.length+1;
            c_end=document.cookie.indexOf(";",c_start);
            if (c_end==-1) c_end=document.cookie.length;
            return unescape(document.cookie.substring(c_start,c_end));
        }
    }
    return "";
}

function checkCaptcha() {
    // check captcha
    var sInsCaptcha = calcMD5($('.captcha').val());
    var sCookieCaptcha = getCookie('strSec');

    if (sInsCaptcha == sCookieCaptcha) {
        alert('Wow, great, you enter correct captcha');
    } else {
        alert('Sorry, but wrong');
    }
}

We will using getCookie function to get cookies information, and checkCaptcha to validate captcha.

js/jquery.min.js and js/md5.js

This is common files – jQuery library with addon for working with MD5. No need to give full code of that file here. It always available as a download package

Step 4. PHP

Ok, here are all used PHP file:

index.php

PHP
<?php

ob_start();
$chars = array("a","A","b","B","c","C","d","D","e","E","f","F","g","G","h","H","i","I","j","J",
               "k","K","L","m","M","n","N","o","p","P","q","Q","r","R","s","S","t","T",
               "u","U","v","V","w","W","x","X","y","Y","z","Z","2","3","4","5","6","7","8","9");

$textstr = '';
for ($i = 0, $length = 5; $i < $length; $i++)
   $textstr .= $chars[rand(0, count($chars) - 1)];

$hashtext = md5($textstr);
setcookie('strSec', $hashtext, 0, '/');
if (produceCaptchaImage($textstr) != IMAGE_ERROR_SUCCESS) {
    // output header
    header( "Content-Type: image/gif" );

    header("Expires: Mon, 21 Jul 2010 05:00:00 GMT");
    header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
    header("Cache-Control: no-store, no-cache, must-revalidate");
    header("Cache-Control: post-check=0, pre-check=0", false);
    header("Pragma: no-cache" );

    // output error image
    @readfile('captcha/captcha_error.gif');
}
ob_end_flush();


function produceCaptchaImage($text) {
    // constant values
    $backgroundSizeX = 2000;
    $backgroundSizeY = 350;
    $sizeX = 200;
    $sizeY = 50;
    $fontFile = "captcha/verdana.ttf";
    $textLength = strlen($text);

    // generate random security values
    $backgroundOffsetX = rand(0, $backgroundSizeX - $sizeX - 1);
    $backgroundOffsetY = rand(0, $backgroundSizeY - $sizeY - 1);
    $angle = rand(-5, 5);
    $fontColorR = rand(0, 127);
    $fontColorG = rand(0, 127);
    $fontColorB = rand(0, 127);

    $fontSize = rand(14, 24);
    $textX = rand(0, (int)($sizeX - 0.9 * $textLength * $fontSize)); // these coefficients are empiric
    $textY = rand((int)(1.25 * $fontSize), (int)($sizeY - 0.2 * $fontSize)); // don't try to learn how they were taken out

    $gdInfoArray = gd_info();
    if (! $gdInfoArray['PNG Support'])
        return IMAGE_ERROR_GD_TYPE_NOT_SUPPORTED;

    // create image with background
    $src_im = imagecreatefrompng( "captcha/background.png");
    if (function_exists('imagecreatetruecolor')) {
        // this is more qualitative function, but it doesn't exist in old GD
        $dst_im = imagecreatetruecolor($sizeX, $sizeY);
        $resizeResult = imagecopyresampled($dst_im, $src_im, 0, 0, $backgroundOffsetX, $backgroundOffsetY, $sizeX, $sizeY, $sizeX, $sizeY);
    } else {
        // this is for old GD versions
        $dst_im = imagecreate( $sizeX, $sizeY );
        $resizeResult = imagecopyresized($dst_im, $src_im, 0, 0, $backgroundOffsetX, $backgroundOffsetY, $sizeX, $sizeY, $sizeX, $sizeY);
    }

    if (! $resizeResult)
        return IMAGE_ERROR_GD_RESIZE_ERROR;

    // write text on image
    if (! function_exists('imagettftext'))
        return IMAGE_ERROR_GD_TTF_NOT_SUPPORTED;
    $color = imagecolorallocate($dst_im, $fontColorR, $fontColorG, $fontColorB);
    imagettftext($dst_im, $fontSize, -$angle, $textX, $textY, $color, $fontFile, $text);

    // output header
    header("Content-Type: image/png");

    // output image
    imagepng($dst_im);

    // free memory
    imagedestroy($src_im);
    imagedestroy($dst_im);

    return IMAGE_ERROR_SUCCESS;
}

?>

Here are several comments by code:

In start we will define array of used characters – $chars. After, we will compose string (5 letters) with random characters. After, will MD5 this result string and save it into cookies. We will use this to more security. So any bot or special script will unable to find this key even after detail investigation of result page.

produceCaptchaImage is main function which will draw our captcha. It take just 1 param – text which we going to draw. We will using GD library to draw. We will randomly choose position to text in captcha, angle (+- 5 degrees), font color. After will get our background image which will mage reading of our code more difficult, and, will draw result text on this image.

 

Conclusion

Today I told you how to create our own captcha – security image. I sure that you will use it in your projects. Quite any serious project using it to protect spamming. Good luck!