PHP
Article
By Mehul Jain

Simple Captchas with PHP and GD

By Mehul Jain

sample image

By now, we’ve all encountered captcha images in online forms. Captchas are a necessary evil, and this article will teach you how they’re made.

Please note that while there are better, automatic third party solutions for captchas out there such as ReCaptcha, this tutorial aims merely to explain and demonstrate how such technology actually works. We won’t be explaining what captchas actually are, as it’s assumed to be common knowledge and already covered in greater detail elsewhere.

Drawing captchas

You must have the GD(Graphics Draw) library installed before proceeding. This library enables drawing of graphics and images through built-in PHP functions. To install it, run sudo apt-get install php5-gd or if on non-Ubuntu-based operating systems, follow instructions.

Captchas are usually made up of 3 things – shape, distortion, and the text.
We’ll follow the steps mentioned below:

  1. Display an empty image on the browser.
  2. Create a shape.
  3. Generate random lines.
  4. Generate random dots.
  5. Generate random text.

The procedural style used in this article is present only because this is a proof of concept, and to keep the final file as simple as possible. In a real project, you would go OOP.

Display an empty image

The image will be treated by HTML as if an external image is being displayed using the “img” tag. Two functions are used – one for creating the image and another for displaying.

<?php
session_start();
?>

    <title>demo.php</title>
    <body style="background-color:#ddd; ">

    <?php
    create_image();
    display();
    /***** definition of functions *****/
    function display()
    {
        ?>

        <div style="text-align:center;">
            <h3>TYPE THE TEXT YOU SEE IN THE IMAGE</h3>
            <b>This is just to check if you are a robot</b>

            <div style="display:block;margin-bottom:20px;margin-top:20px;">
                <img src="image.png">
            </div>
            //div1 ends
        </div>                          //div2 ends

    <?php
    }

    function  create_image()
    {
        $image = imagecreatetruecolor(200, 50);
        imagepng($image, "image.png");
    }

    ?>
    </body>
<?php
?>

The first line indicates the start of the user’s session on our page.

The display() function has nothing other than a normal HTML code that displays an image in the browser. Other than that, only styling is done for the output to look presentable.

Inside the create_image() function, a variable is used to refer the image returned by the imagecreatetruecolor() function which takes the width and length of the image as its arguments. imagepng() creates a png image of the specified name and path (in the same directory).

A black image will be the output after our first step.

scrn1

Note that the function imagepng() will be the last line of our function and all the following steps are to be inserted in the create_image() function before this function call only, else they would not take effect.

Create a shape

Any shape can be chosen for the captcha. We’ll choose a rectangle by using the function imagefilledrectangle(). It takes five arguments – image reference, starting x-pos, starting y-pos, ending x-pos, ending y-pos, and the background color. You may use the corresponding function for an ellipse for generating elliptical captcha.

The imagecolorallocate() function allocates a color to a variable as it takes the RGB combination of the color as arguments. The following code is to be appended in the create() function.

$background_color = imagecolorallocate($image, 255, 255, 255);  
imagefilledrectangle($image,0,0,200,50,$background_color);

The previous image will be white after this step.

sample image

Generate random lines.

Now, we actually start with making the distortion part of the captcha. In PHP, the lines are generated from the starting point(x1,y1) to the end point(x2,y2). Now as we want our lines to touch both ends of the box, we will keep the <x1,x2> coordinates as <0,200> i.e., the complete width of our box. The <y1,y2> coordinates will be randomly generated. This will create just one random line. We will generate multiple lines by putting this functionality inside a for loop.

$line_color = imagecolorallocate($image, 64,64,64); 
for($i=0;$i<10;$i++) {
    imageline($image,0,rand()%50,200,rand()%50,$line_color);
}

The imageline() function takes the x1,x2,y1,y2 coordinates as arguments in that order apart from the image reference and color of the line. The line color has been allocated just as the background color had been allocated in the previous step.

The y-coordinate is given as rand()*%50 because this is the height of our box and will always return a value under 50. You may alternatively use rand(0,50). They will yield the same output range.

sample image

Generate random dots.

Random dots will be generated in the same way as random lines. The function used is imagesetpixel(). This function takes the value of coordinates where the dot will be placed in the box.

$pixel_color = imagecolorallocate($image, 0,0,255);
for($i=0;$i<1000;$i++) {
    imagesetpixel($image,rand()%200,rand()%50,$pixel_color);
}  

The x-coordinate is randomly generated by using rand()*%200 as this is the width of our box and this will always return a value under 200. You may alternatively use rand(0,200). They will yield the same output range. The y coordinate is generated as in the lines step.

sample image

Generate random text

We will randomly point to a position in the string (which contains the alphabet in both lower and upper case) and assign it to the variable $letter

$letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$len = strlen($letters);
$letter = $letters[rand(0, $len-1)];

$text_color = imagecolorallocate($image, 0,0,0);

When put inside a loop, it looks like this-

for ($i = 0; $i< 6;$i++) {
    $letter = $letters[rand(0, $len-1)];
    imagestring($image, 5,  5+($i*30), 20, $letter, $text_color);
    $word.=$letter;
}
$_SESSION['captcha_string'] = $word;

We will explain the lines

$word.=$letter;
$_SESSION['captcha_string'] = $word;   

in the next section.

The function imagestring() writes the text in our image. It has 6 arguments:

  1. Image reference.
  2. Font-size of the text (it can be 5 at most).
  3. x-coordinate (changing proportionally for every alphabet).
  4. y-coordinate (kept the same, although we could change this randomly too).
  5. Actual string to be written.
  6. Font-color of the text.

You can also use the function imagettftext() if you wish to have a bigger font and different font style. It takes 2 additional arguments for angle and font style of the text.

Calculation of x-coordinate is done by inspection. Roughly, the letters are spaced about 35 pixels (5+($i*30)) where $i=0,1,2,3,4,5,6. This is because if we had kept this value around 15-20px, there would have been a possibility of two letters overlapping. If the value had been more than 40px, the letters altogether would have not fit into the box.

This will generate a 6 alphabet captcha text. We can always create more randomness by changing the aspects that have been kept constant due to simplicity, like color, y-coordinates etc.

The final captcha will look like this

sample image

The text written in the captcha will change every time you refresh the page.
More randomness can be achieved by creating designs with the pixels or by changing the color or size.

--ADVERTISEMENT--

Validating

It is here that the user’s response is taken and after processing it, he/she receives a reply. At first, a simple form is made with an input textbox and a submit button. There can be many ways of processing a captcha as per the requirements of complex web applications. But keeping it simple for the sake of this example, we’ll process it on the same page.

The two lines left unexplained in the previous code snippets come into play now:

  1. $word.=$letter; – the concatenation operator . is used to append all the individual letters one after another, generating the 6-letter word.
  2. $_SESSION['captcha_string'] = $word; Our captcha string is stored in a session variable which will be used for validation purposes.

We’ll change the definition of display() to add a form-like structure.

Two submit buttons will be used, one to submit the string and another other to refresh the page.

The following lines will be added in between the two closing div tags (see comments in the previous display() function)

function display()
{
    ?>

    <div style="text-align:center;">
        <h3>TYPE THE TEXT YOU SEE IN THE IMAGE</h3>
        <b>This is just to check if you are a robot</b>

        <div style="display:block;margin-bottom:20px;margin-top:20px;">
            <img src="image<?php echo $_SESSION['count'] ?>.png">
        </div>
        <form action=" <?php echo $_SERVER['PHP_SELF']; ?>" method="POST"
        / >
        <input type="text" name="input"/>
        <input type="hidden" name="flag" value="1"/>
        <input type="submit" value="submit" name="submit"/>
        </form>

        <form action=" <?php echo $_SERVER['PHP_SELF']; ?>" method="POST">
            <input type="submit" value="refresh the page">
        </form>
    </div>

<?php
}

Before moving further we must know when to display and when not to display the input box. It will be displayed only

  1. if the page has just loaded.
  2. if the user’s answer was incorrect.

The first condition is met by using a $flag which is set to ‘1’ each time the submit button is clicked. Initially, it has been set to any other value. The second condition is achieved by checking if the value stored in our session variable is the same as user input (see the code below).

To achieve this, we will replace the following lines of our starting step at the beginning of the article:

    create_image();
    display();

with:

$flag = 5;

if (isset($_POST["flag"])) //  check that POST variable is not empty
{
    $input = $_POST["input"];
    $flag = $_POST["flag"];
}

if ($flag == 1) // submit has been clicked
{
    if (isset($_SESSION['captcha_string']) && $input == $_SESSION['captcha_string']) // user input and captcha string are same
    {

        ?>

        <div style="text-align:center;">
            <h1>Your answer is correct!</h1>

            <form action=" <?php echo $_SERVER['PHP_SELF']; ?>" method="POST"> // refresh the page
                <input type="submit" value="refresh the page">
            </form>
        </div>

    <?php

    } else // incorrect answer, captcha shown again
    {

        ?>
        <div style="text-align:center;">
            <h1>Your answer is incorrect!<br>please try again </h1>
        </div>
        <?php
        create_image();
        display();
    }

} else // page has just been loaded
{
    create_image();
    display();
}

Note that the functions create_image() and display() are called only as per the 2 conditions discussed above.

We’ll need the session variable from the previous page, so the session is not destroyed here. The session will be automatically destroyed once the browser window is closed.

The captcha will look like this-

sample image

If the input is incorrect, only then user will be prompted again.

sample image

If the input is correct, user will be shown a message.

sample image

There is a minor caveat – when the user presses the back button, any image already present in the cache of the browser will not reload, while the page does. In a POST request, browser back button will show a “document expired” page, but when the request is GET, the image does not regenerate.

The solution is simple – creating unique names of images every time, so that the browser doesn’t find them in cache. We will append a unique string returned to us by the built-in time() function to the image name while creating and while displaying in the browser.

Add this line just below where you started your session:

$_SESSION['count']=time(); // unique string stored

Replace the img src tag in the display() function with

<img src="image<?php echo $_SESSION['count']?>.png">

And the part where we created the png image in create_image() function will also be replaced with

imagepng($image,"image".$_SESSION['count'].".png");

The images will now be called something like image39342015.png. This procedure will create images as many times as the page is refreshed which can waste huge amounts of disk space, so we’ll make sure that before creating an image, all other images of the png extension are deleted. Add the following just before the imagepng() function is called.

$images = glob("*.png");
foreach($images as $image_to_delete)
{
    unlink($image_to_delete);      
}

In a production app, just make sure that you isolate the folder where captcha images are being stored else other useful images may get deleted too.

Download the full code here.

Conclusion

Making various types of captchas in PHP is very easy. This article covered the three basic things used for creating a standard captcha – shape, distortion, and text. This article was a proof of concept, and the code presented herein should not be used in production – especially since excellent alternatives such as ReCaptcha exist, which also supports sound output to help people with hearing impairments. We hope you found this article interesting. Leave your comments and feedback below!

Login or Create Account to Comment
Login Create Account
Recommended
Sponsors
Get the most important and interesting stories in tech. Straight to your inbox, daily.