Create A Simple Image Captcha using PHP

Create A Simple Image Captcha using PHP

I built a custom image captcha using PHP, and I will show you the processes involved.

OUR FILE STRUCTURE

image.png

  • Let's start with the core folder.

We will create two files. One of them will generate the captcha image for us and the other will enable us to verify the generated captcha

Captcha_image.php

We need to generate our captcha text, hash it with md5(), then store the hashed value in a session for verification.

In our function, you may specify how many characters the user gets to fill out.

//Start session
session_start();

//Generate random captcha text
function generateRandomString($length = 10) {
    /*
    * @length: The length of the Captcha Text
    */
    $characters = 'a0bcde1fghi2jklm3nopq4rstu5vwx6yzA7BCDE8FGHIJ9KLMN3OPQRS2TUVW1XYZ0';
    $charactersLength = strlen($characters);

    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}

//we need 5 characters max
$captcha_txt = generateRandomString(5);

Now, there's an interesting something I did with this script.

We will generate an image using PHP, create an array that will store the random background colors of the generated image, then we will pick a random color from the array.

//Start session
session_start();

//Generate random captcha text
function generateRandomString($length = 10) {
    /*
    * @length: The length of the Captcha Text
    */
    $characters = 'a0bcde1fghi2jklm3nopq4rstu5vwx6yzA7BCDE8FGHIJ9KLMN3OPQRS2TUVW1XYZ0';
    $charactersLength = strlen($characters);

    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}
//we need 5 characters max
$captcha_txt = generateRandomString(5);
//generate image with a width of 200px and height of 20px
$image = imagecreate(200, 20);
//random background image
$a=array(
"red"=>[255, 0, 0],
"green"=>[0, 155, 6],
"blue"=>[0, 0, 255],
"black"=>[0,0,0]
);
//pick random color from the array
$color_index= (array_rand($a,1));

Now we will use the function imagecolorallocate() to allocate the background color of our image, keeping in mind our array indexes.

//Start session
session_start();
//Generate random captcha text
function generateRandomString($length = 10) {
    /*
    * @length: The length of the Captcha Text
    */
    $characters = 'a0bcde1fghi2jklm3nopq4rstu5vwx6yzA7BCDE8FGHIJ9KLMN3OPQRS2TUVW1XYZ0';
    $charactersLength = strlen($characters);

    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}
//we need 5 characters max
$captcha_txt = generateRandomString(5);
//generate image with a width of 200px and height of 20px
$image = imagecreate(200, 20);
//random background image
$a=array(
"red"=>[255, 0, 0],
"green"=>[0, 155, 6],
"blue"=>[0, 0, 255],
"black"=>[0,0,0]
);

//pick random color from the array
$color_index= (array_rand($a,1));

//allocate background color with indexes from variable $color_index
$bk_clr = imagecolorallocate($image, $a[$color_index][0], $a[$color_index][1], $a[$color_index][2]);
//Final image color, set the text color to white
$img_clr = imagecolorallocate($image, 255, 255, 255); //this will include the background color and the text color

Now we need to draw our image string using the function imagestring().

We will pass in the parameters;

  • $image - the generated image
  • 5 - Inbuilt font selection
  • 50 - the x coordinate of the upper left corner
  • 0 - the y coordinate of the upper left corner ( we need it to be centered )
  • $captcha_txt - the captcha text challenge
  • $img_clr - the captcha image color
//Start session
session_start();
//Generate random captcha text
function generateRandomString($length = 10) {
    /*
    * @length: The length of the Captcha Text
    */
    $characters = 'a0bcde1fghi2jklm3nopq4rstu5vwx6yzA7BCDE8FGHIJ9KLMN3OPQRS2TUVW1XYZ0';
    $charactersLength = strlen($characters);

    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}
//we need 5 characters max
$captcha_txt = generateRandomString(5);
//generate image with a width of 200px and height of 20px
$image = imagecreate(200, 20);
//random background image
$a=array(
"red"=>[255, 0, 0],
"green"=>[0, 155, 6],
"blue"=>[0, 0, 255],
"black"=>[0,0,0]
);

//pick random color from the array
$color_index= (array_rand($a,1));

//allocate background color with indexes from variable $color_index
$bk_clr = imagecolorallocate($image, $a[$color_index][0], $a[$color_index][1], $a[$color_index][2]);
//Final image color, set the text color to white
$img_clr = imagecolorallocate($image, 255, 255, 255); //this will include the background color and the text color

//Draw the image
imagestring($image, 5, 50, 0, $captcha_txt, $img_clr);

Now, we need to set up an expiry time for the captcha text, hash the expiry time with the captcha text, then store it in a session variable.

//Start session
session_start();

//Generate random captcha text
function generateRandomString($length = 10) {
    /*
    * @length: The length of the Captcha Text
    */
    $characters = 'a0bcde1fghi2jklm3nopq4rstu5vwx6yzA7BCDE8FGHIJ9KLMN3OPQRS2TUVW1XYZ0';
    $charactersLength = strlen($characters);

    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}
//we need 5 characters max
$captcha_txt = generateRandomString(5);
//generate image with a width of 200px and height of 20px
$image = imagecreate(200, 20);
//random background image
$a=array(
"red"=>[255, 0, 0],
"green"=>[0, 155, 6],
"blue"=>[0, 0, 255],
"black"=>[0,0,0]
);
//pick random color from the array
$color_index= (array_rand($a,1));

//allocate background color with indexes from variable $color_index
$bk_clr = imagecolorallocate($image, $a[$color_index][0], $a[$color_index][1], $a[$color_index][2]);
//Final image color, set the text color to white
$img_clr = imagecolorallocate($image, 255, 255, 255); //this will include the background color and the text color

//Draw the image
imagestring($image, 5, 50, 0, $captcha_txt, $img_clr);

//Expiry time of 3 minutes
$expire = gmdate(strtotime('+3 minutes', time())); 
//concatenate the expiry time with the captcha text
$tmp_hash = $captcha_txt.$expire;

$_SESSION['captcha_set'] = TRUE;  //makes sure that the captcha your'e verifying is set
$_SESSION['captcha_token'] =  md5($tmp_hash); //hash the captcha
$_SESSION['captcha_expire'] = $expire; //expiry time

Lastly, we need to convert the image string we had generated using imagestring() to a png image, then free any memory related to the image by destroying our image.

//Start session
session_start();
//Generate random captcha text
function generateRandomString($length = 10) {
    /*
    * @length: The length of the Captcha Text
    */
    $characters = 'a0bcde1fghi2jklm3nopq4rstu5vwx6yzA7BCDE8FGHIJ9KLMN3OPQRS2TUVW1XYZ0';
    $charactersLength = strlen($characters);

    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}
//we need 5 characters max
$captcha_txt = generateRandomString(5);
//generate image with a width of 200px and height of 20px
$image = imagecreate(200, 20);
//random background image
$a=array(
"red"=>[255, 0, 0],
"green"=>[0, 155, 6],
"blue"=>[0, 0, 255],
"black"=>[0,0,0]
);

//pick random color from the array
$color_index= (array_rand($a,1));

//allocate background color with indexes from variable $color_index
$bk_clr = imagecolorallocate($image, $a[$color_index][0], $a[$color_index][1], $a[$color_index][2]);
//Final image color, set the text color to white
$img_clr = imagecolorallocate($image, 255, 255, 255); //this will include the background color and the text color

//Draw the image
imagestring($image, 5, 50, 0, $captcha_txt, $img_clr);

//Expiry time of 3 minutes
$expire = gmdate(strtotime('+3 minutes', time())); 
//concatenate the expiry time with the captcha text
$tmp_hash = $captcha_txt.$expire;

$_SESSION['captcha_set'] = TRUE;  //makes sure that the captcha your'e verifying is set
$_SESSION['captcha_token'] =  md5($tmp_hash); //hash the captcha
$_SESSION['captcha_expire'] = $expire; //expiry time

//convert the image string to a png
imagepng($image);
//Free memory
imagedestroy($image);

At this point, we are done with captcha_image file.

Let's save the file now, and create a new one to verify our captcha.

Captcha_verify.php

We will declare a function that will accept just one parameter which is the user input, then set status codes as our return value.

//start session
session_start();
function verifyCaptcha($input = "abc") {
    /**
     * verifyCaptcha : This function verifies the validity of Captcha Images
     * @input : The user input
     * @return : 407 (captcha expired), 200 (captcha verified), 400 (captcha unverified), 500 (Server Error)
     **/
}

STEP 1

Check if the 3 session variables exist, then assign their values to our local variables.

//start session
session_start();

function verifyCaptcha($input = "abc") {
    /**
     * verifyCaptcha : This function verifies the validity of Captcha Images
     * @input : The user input
     * @return : 407 (captcha expired), 200 (captcha verified), 400 (captcha unverified), 500 (Server Error)
     **/

  $date = time();
    if(isset($_SESSION['captcha_set']) && 
        isset($_SESSION['captcha_token']) &&
            isset($_SESSION['captcha_expire'])) {
                //assign values
                $captcha_expire = $_SESSION['captcha_expire'] ;
                $captcha_token = $_SESSION['captcha_token'];
            }else{
                $return = array(
                "captcha_status" => 500,
                "captcha_message" => "Session Invalid!",
                 );
                return json_encode($return);
            }
}

STEP 2

  • We will check if the captcha is still valid ( ie it hasn't expired ).
  • Rehash the user input md5($input.$captcha_expire).
  • Then compare it with the one from our session.

If it matches, we will return a status code of 200 which means that our captcha verification was successful. But if it doesn't match, we will return a status code of 400.

//start session
session_start();

function verifyCaptcha($input = "abc") {
    /**
     * verifyCaptcha : This function verifies the validity of Captcha Images
     * @input : The user input
     * @return : 407 (captcha expired), 200 (captcha verified), 400 (captcha unverified), 500 (Server Error)
     **/

  $date = time();
    if($_SESSION['captcha_set'] &&
        isset($_SESSION['captcha_token']) &&
            isset($_SESSION['captcha_expire'])) {
                //assign values
                $captcha_expire = $_SESSION['captcha_expire'] ;
                $captcha_token = $_SESSION['captcha_token'];
                //check if captcha has expired
               if($date < $_SESSION['captcha_expire']){
                    if(md5($input.$captcha_expire) == $captcha_token){
                        unset( $_SESSION['captcha_expire'] );
                        unset( $_SESSION['captcha_token']);
                        unset($_SESSION['captcha_set']);
                        $return = array(
                            "captcha_status" => 200,
                            "captcha_message" => "Captcha Verified!",
                        );
                        return json_encode($return); 
                    } else {
                        $return = array(
                            "captcha_status" => 400,
                            "captcha_message" => "Captcha Verification Failed!",
                         );
                         return json_encode($return);
                    }

                }else{
                    $return = array(
                    "captcha_status" => 407,
                    "captcha_message" => "Captcha Expired!",
                    );
                    return json_encode($return);
               }
            }else{
                $return = array(
                "captcha_status" => 500,
                "captcha_message" => "Session Invalid!",
                 );
                return json_encode($return);
            }
}

Now save the file and create an index page where we will display our captcha to the user

Index.php

Here are the things we will do

  • We will create an HTML img tag and set the attribute (src) to core/captcha_image.php.
  • We will create a form for the user to fill in the text from the image they see.
  • We will process the form input as soon as the user hits the submit button, then show the result of our verification to the user
<?php
//require the verification file
require_once 'core/captcha_verify.php';

if(isset($_POST)&& isset($_POST['submit'])){
//call the function by binding it to a variable
$verify_captcha = json_decode(verifyCaptcha($_POST['captcha']), true); 

     if ($verify_captcha['captcha_status'] == 200) {
         //If captcha verification is Successful
         echo '<script>   
            document.addEventListener(\'DOMContentLoaded\', (event) => {
            alert("'.$verify_captcha['captcha_message'].'");
            })</script>';
         } else {
        //If Unsuccessful
        echo '<script>   
             document.addEventListener(\'DOMContentLoaded\', (event) => {
        alert("'.$verify_captcha['captcha_message'].'");
        })</script>';
        }
}
?>
<html>

  <div id="captcha_img_house" style="text-align:center;margin-bottom:10px">
        <img id="captcha_img" src="core/captcha_image.php">   
    </div>

<form method="post" style="style="text-align: center;"> 
        <label id="captcha_form_label">Enter the Text You See</label>
        <br>
    <input id="captcha_key" name="captcha">
    <button id="btn_captcha_submit" type="submit" name="submit">Verify</button>
</form>

</html>

Now, let's test our script.

Now I will provide two different inputs and purposely leave the captcha unattended for 4 minutes so I will show you how the function works.

  • The Captcha Challenge

image.png

  • Failed Challenge

image.png

  • Successful challenge

image.png

  • Expired Challenge

image.png

ASSETS FOLDER

I have shown you the basic functions that we need. However, you may expand your captcha script to automatically switch captcha images, in case the user can't see through the current captcha image.

You may also define some styling for your captcha.

Here's how I customized mine

Here are the status codes you should be aware of.

//Output 1
$return = [
'captcha_status' => 200,
'captcha_message' => 'Captcha Verified'
];

//Output 2
$return = [
'captcha_status' => 400,
'captcha_message' => 'Captcha Verification failed'
];

//Output 3
$return = [
'captcha_status' => 407,
'captcha_message' => 'Captcha Expired'
];

//Output 4
$return = [
'captcha_status' => 500,
'captcha_message' => 'Session Invalid'
];

You've reached the end of my article.

Thank You!