Helpful Information
 
 
Category: Coding tips & tutorials threads
Creating Image validation for Forms

Create image validation

In this tutorial:

Create your own (simple) version of CAPTCHA
Learn about PHP image functions


What you need:

A basic knowledge of PHP
A PHP Enabled server
30 minutes


What is image validation?

You've most likely seen by those annoying forms with CAPTCHA-- "Please type the characters in the image" they say. The concept behind them is simple: computers cannot read like humans can, so by forcing a user to read something and copy it, it should prevent computers from posting information to a server. While some of them are extremely hard to read, they generally work well for weeding out the pesky web bots. Actually, they also effectively block form submissions that are not coming from your website. No, they aren't perfect, but they generally work well.

So, what are we waiting for? Lets make a simple one using PHP to help protect our forms.

Our string

First, we need to generate the characters to place in our image. Take a look at this:


<?php
SESSION_START();
$_SESSION['captcha'] = '';
$captcha_chars = array('2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','J','K','M','N','P','Q','R','S','Y','U','V','W','X','Y','Z');
for($i = 0; $i < 5; $i++){
$rand_char = rand(0, (count($captcha_chars) -1));
$_SESSION['captcha'] .= $captcha_chars[$rand_char];
}
?>

(Notice that similar characters like 0 and O, and I and 1 have been removed from the array so the user does not get confused.)

That block of code should be self explanatory, but just in case, I'll briefly explain. We are starting a session to store the string in so that our image and the form validation process can have access to the string. Then, we generate five random characters via the rand() function.

With that out of the way, we are going to need to use PHP's relatively decent image functions (http://us3.php.net/manual/en/ref.image.php).

The image

Let's create an image. To do this, we start by calling the function createimage().


$im = imagecreate(200, 60);

Here we have declared our image to be 200px in width, and 60px in height.

Lets create some colors for our image:


$bg_color = imagecolorallocate($im, 0, 122, 87); //This is the bg color
$font_color = imagecolorallocate($im, 0, 0, 0);


Okay, lets stop and see what we've done so far. By adding three more lines of code to the end, we can see our image:


<?php
SESSION_START();
$_SESSION['captcha'] = '';
$captcha_chars = array('2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','J','K','M','N','P','Q','R','S','Y','U','V','W','X','Y','Z');
for($i = 0; $i < 5; $i++){
$rand_char = rand(0, (count($captcha_chars) -1));
$_SESSION['captcha'] .= $captcha_chars[$rand_char];
}

$im = imagecreate(200, 60);

$bg_color = imagecolorallocate($im, 0, 122, 87);
$font_color = imagecolorallocate($im, 0, 0, 0);

header("Content-type: image/png"); // We are telling the browser that there is an image coming
imagepng($im); // Now we are outputting the image
imagedestroy($im); // Now we are deleting the image resource
?>


Wasn't that fun? :p Hopefully the code is working and you see a small image. If not, now is the time to rewind the tape and correct your mistakes so far.

Drum roll please. We are about to add the text! First, we need to change the string into an array of single characters. I prefer preg_split(), but you can use whatever you like:


$characters = preg_split("//", $_SESSION['captcha'], -1, PREG_SPLIT_NO_EMPTY);

(Notice the PREG_SPLIT_NO_EMPTY flag on the end-- we don't want any blank characters).

And here is where it gets tricky. Find the Arial font and place it in the folder with the script, because we need it for inserting the text. Once you've done that, your ready for this:


$x = 5; // Start the characters 5px from left
foreach($characters as $char){
$y = rand(25,45); // Y cord or char
$size = rand(16,28); // Font size for char
$rotation = rand(-17,17); // Rotation of char
// INSERT CHAR HERE IN A MINUTE
$x += 38; // X cord of char
}


So far all this does is set of some random params for the character to be inserted. To insert the text, we use a somewhat confusing function called imagettftext(). Here is how we will use it:

imagettftext($im, $size, $rotation, $x, $y, $font_color, './arial.ttf', $char);

The big finish

Thus, our finished code is something like this:

<?php
SESSION_START();
$_SESSION['captcha'] = '';
$captcha_chars = array('2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','J','K','M','N','P','Q','R','S','Y','U','V','W','X','Y','Z');
for($i = 0; $i < 5; $i++){
$rand_char = rand(0, (count($captcha_chars) -1));
$_SESSION['captcha'] .= $captcha_chars[$rand_char];
}

$im = imagecreate(200, 60);

$bg_color = imagecolorallocate($im, 0, 122, 87);
$font_color = imagecolorallocate($im, 0, 0, 0);


$characters = preg_split("//", $_SESSION['captcha'], -1, PREG_SPLIT_NO_EMPTY);

$x = 5;
foreach($characters as $char){
$y = rand(25,45);
$size = rand(16,28);
$rotation = rand(-17,17);
imagettftext($im, $size, $rotation, $x, $y, $font_color, './arial.ttf', $char);
$x += 38;
}

header("Content-type: image/png");
imagepng($im);
imagedestroy($im);

?>

If you want to get fancy, you can add random lines or a background image, but that's beyond this tutorial.

To insert the image into your form, place this next to the text input in which you wish the have the user type the captcha string:

<img src="./path_to_script/script_name.php" />

To check against the users input, do something like this in the form process:

if($_POST['captcha'] != $_SESSION['captcha']){die('Incorrect captcha entry');}

Thanks for reading this, and I hope you learned something.

EDIT:
----------------------
djr33 was kind enough to put a modified version of the script online. The online version features some distortion.
http://ci-pro.com/misc/jascaptcha -- View retult
http://ci-pro.com/misc/jascaptcha/code.txt -- View code

Another way would be to add a hidden field like this:


<input type="hidden" name="comfirm_email">

And since it's hidden, humans wont see it, but bots will fill it out. :D
I made my own little "big finish":


<?php
SESSION_START();
$_SESSION['captcha'] = '';
$captcha_chars = array('2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','J','K','M','N','P','Q','R','S','Y','U','V','W','X','Y','Z');
for($i = 0; $i < 5; $i++){
$rand_char = rand(0, (count($captcha_chars) -1));
$_SESSION['captcha'] .= $captcha_chars[$rand_char];
}
header("Content-type: image/png");
$im = imagecreate(200, 60);

$bg_color = imagecolorallocate($im, 0, 122, 87);

$characters = preg_split("//", $_SESSION['captcha'], -1, PREG_SPLIT_NO_EMPTY);

$x = 5;
foreach($characters as $char){
$y = rand(25,45);
$size = rand(16,28);
$rotation = rand(-17,17);
imagettftext($im, $size, $rotation, $x, $y, imagecolorallocate($im, rand(0,20), rand(0,210), rand(0,20)), 'phpower/fonts/arial.ttf', $char);
imageline($im, $size*rand(0,5), $rotation-rand(0,5), $x+rand(0,3), $y-rand(0,3), imagecolorallocate($im, rand(0,122), rand(0,122), rand(0,122)));
$points = array(rand(0,200), rand(0,200), rand(0,200),rand(0,200), rand(0,200), rand(0,200), rand(0,200), rand(0,200), rand(0,200), rand(0,200));
$vertices = count($points) / 2;
imagepolygon($im, $points, $vertices, imagecolorallocate($im, rand(0,255), rand(0,255), rand(0,255)));

$x += 38;
}

imagepng($im);
imagedestroy($im);

?>

Interesting thought, Nile. Do bots fill out hidden feilds, or would you need to do something more like:


<input type="text" name="comfirm_email" style="display:none;">

?

I think either would work. Editted my above post for my own "big finish".

Glad my code inspired you :) I wasn't going to go that far in the tutorial, but what the heck. Two things, though: 1) most of the lines and polygons should probably be inserted before the characters, otherwise it will cover the characters which might make them really hard to read. 2) You might want to make your code more readable for those just starting with image functions. (i.e. generate the numbers outside of the functions, add comments, etc.).

That is a great contribution to the tutorial. :)

Bots aren't intelligent enough to go looking for a page and try to break the encryption. That's not the strategy.
A bot is likely custom designed for a page.

So if it can be programmed to ignore your "trick" then that's not very useful. Sure, it's a bit better than the standard spam, but that can be accomplished with minimal effort, like "check this box to show you're not a bot!".

Do you have a demo page up for the captcha?

I wrote a couple distortion filters for captchas to improve them on my forum, at one point. I could add those here, if it needs it.

No demo page, sorry. I don't have any web space as of yet to do things like that. If someone would like to volunteer to post the code on their site, I would very much appreciate it. Just let me know via a post or PM so I can link to it in the original post.

As far as the distortion filters go, my intention was to keep the tutorial as simple as possible to give people an idea of how programs like CAPTCHA work (kind of a point in the right direction, if you will). If you think it's simple enough for this tutorial, please feel free to post that information. I would like to see the code myself :). (Right now all the code does is create random characters that are random sizes and position them randomly. All the characters are clearly visible, though.)

Ok, here's a demo, including the distortion.

http://ci-pro.com/misc/jascaptcha
http://ci-pro.com/misc/jascaptcha/code.txt

The values are all adjustable. I'll try to write up a bit on that later.

Thanks, Daniel, but there is a serious problem with the script. Since you are generating both the bg and the fg randomly, sometimes the colors are similar and you can't see the text. When I viewed it, 2 out of ten could not be seen clearly enough to make out.

I don't have time now, but I can try to write something up to fix that later, if you like. I am thinking that you just need to generate three random numbers (r, g, b) for the bg color, and create an algorythm to invert the color the color for the text.

The inverted color is already used for the random lines.

I'm aware of the problem, too, but I didn't have time to think about it too much. Basically, you can loop through random colors to be sure the contrast is high enough. That's possible.










privacy (GDPR)