Security Images in PHP - Advanced

Security Images in PHP
By N Rohler

Introduction

Due to the amazing power of computers today,
they are able to behave almost like humans. Programs can be written to interact
with many websites. Unfortunately, many of these interactions are ones we don’t
want- including fake sign-ups, spam through contact forms, and stealing places
in line for items such as tickets.

The best way to avoid this is to include confirmations only real humans can
comprehend. One of the most popular methods of doing this is through Security
Images. In a nutshell, security images are dynamically generated images containing
text which is hidden within other graphics. The characters must be entered
correctly in a confirmation field to continue. In this tutorial, we will learn
how to create a security image template, then put it to use.

Prerequisites

Basic PHP skills
Basic GD Graphics Library knowledge (not required,
but helpful)
Basic MySQL/PHP integration skills
Access to PHP server with GD graphics library (included with
PHP 4.3.x)
Access to MySQL database
Included
files
- securityimage_finished.php, signupdemo_finished.php,
bg1.png, bg2.png, bg3.png

Preparation

Before beginning, you will need to create a database table in your MySQL database.
Use the following SQL to do so:


CREATE TABLE `security_images` (
   `ID` int(11) NOT NULL auto_increment,
   `insertdate` datetime NOT NULL default ‘0000-00-00 00:00:00′,
   `referenceid` varchar(100) NOT NULL default ”,
   `hiddentext` varchar(100) NOT NULL default ”,
   PRIMARY KEY (`ID`)
) TYPE=MyISAM;

Laying down a plan

I believe the best first-step is to create
a plan. First of all, we will have a signup form, signupdemo.php.
In the form, we will have a security image, a hidden field containing the unique
reference ID to this image, and a confirmation field. The image will be called
from another PHP page, securityimage.php.

When the security image
is requested, the unique reference ID will be passed in the url as refid (i.e.
securityimage.php?refid=abcdefg123hij).
This page will generate a random string of a set length, and output an image
containing the text. Next, the reference ID and the hidden text value will
be entered into the MySQL database table, security_images. Finally,
any records older than one day will be deleted from the table.

When the user submits
the signup form, the handler script will collect the hidden reference ID, and
the entered hidden text. Then, it will check these two values against the database.
If the query doesn’t return 0 records,
the signup is valid. Otherwise, it is invalid, and the user will have to re-enter
the security image text.

Getting Started - Securityimage.php

The first page to create is the page
which will generate the security images, securityimage.php.
First, we need to open the PHP block and determine if we can use the refid passed
in the querystring; otherwise, we generate a unique one.

<?php
//Generate Reference ID

if (isset($HTTP_GET_VARS["refid"]) && $HTTP_GET_VARS["refid"]!="")
{

   $referenceid = stripslashes($HTTP_GET_VARS["refid"]);
} else {
   $referenceid = md5(mktime()*rand());
}

Now, we must select a font to use. For this demo, we will use Century, because
it is easy to distinguish between zeroes and o’s, and, because it is a serif
font, it is harder for a computer to comprehend.
You will need to set the path to the font, reflecting what OS you are using
and where the fonts folder is.

//Select Font
$font = "C:\WINDOWS\Fonts\Century.ttf";

Next, we will randomly select one of the three backgrounds (from the included
files). Using one of these files, we initialize a new image identifier $im,
representing the background image, using ImageCreateFromPNG. If
you want more variety, you can create more backgrounds (must be PNG format).

//Select random background image
$bgurl = rand(1, 3);
$im = ImageCreateFromPNG("images/bg".$bgurl.".png");

After this, we will generate a random string. To do this, we first create an
array of characters to use. Then, we specify a length for the text to be generated.
For this demonstration, we will use 8, but you can change it to any length you
wish. Finally, we create an empty variable, then randomly fill it with characters
in a loop.

//Generate the random string
$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","L","m","M","n","N","o","O","p","P","q","Q","r","R","s","S","t","T","u","U","v",
"V"
,"w","W","x","X","y","Y","z","Z","1","2","3","4","5","6","7","8","9");
$length = 8;
$textstr = "";
for ($i=0; $i<$length; $i++)
{

   $textstr .= $chars[rand(0, count($chars)-1)];
}


Next, we set other text variables. To do so, we first create a random font size
between 12 and 16 points. Then, we create a random angle. Even though angles
should only be positive values, the GD Library is smart enough to correct it
(-5° to 355° for example). A random dark color to be used with the image
is then created, using ImageColorAllocate.

//Create random size, angle, and dark color
$size = rand(12, 16);
$angle = rand(-5, 5);
$color = ImageColorAllocate($im, rand(0, 100), rand(0, 100),
rand(
0, 100));


We will be preparing the text position next. To do this, we will use imagettfbbox to
return the dimensions of the true type text box using the already defined size,
angle, font, and textstr variables. After that, we determine the absolute difference
between 1- lower right corner x position and the lower left corner x position
(the width), and 2- the upper right corner y position and the lower right corner
y position (height). If you’re scratching your head, consider the following diagram:

Then, using the imagesx (width) & imagesy (height)
properties of the image we created earlier, we
generate a central x and y position. Note that we add a random number between
-20 and 20 to the x position, so that it is a little to the left or right of
center.

//Determine text size, and use
dimensions to generate x & y coordinates

$textsize = imagettfbbox($size, $angle, $font, $textstr);
$twidth = abs($textsize[2]-$textsize[0]);
$theight = abs($textsize[5]-$textsize[3]);
$x = (imagesx($im)/2)-($twidth/2)+(rand(-20, 20));
$y = (imagesy($im))-($theight/2);

We’re finally ready to add the text to the
image using all of the variables we have just created. Now, let’s go over
the variables:
$im represents the original image reference
$size represents the font size
$angle represents the angle the text is
to be tilted at
$x represents the x coordinate to place the text at
$y represents the y coordinate to place the text at

$color represents the random color you just created
$font represents the reference to the ttf font file
$textstr represents the text to be inserted

//Add text to image
ImageTTFText($im, $size, $angle, $x, $y, $color, $font, $textstr);

Now that we’ve prepared our image, we’re finally ready to output it. So,
we first set the Content-Type header to be a PNG
image. Then,
we simply write the image content in PNG format using ImagePNG.

//Output PNG Image
header("Content-Type: image/png");
ImagePNG($im);

Deleting the image is very important, because it frees the server memory.
This ensures optimal performance from the server. To do so, we use the imagedelete function,
passing our image reference $im to it.

//Destroy the image to free memory
imagedestroy($im);

The next step is to enter the random text, $textstr,
and the reference ID, $referenceid, into the MySQL
database, using the table we created earlier. To insert the text, we first connect
to the server. Be sure you enter your username
and password in the connection call. Then, we insert the current date and
time, reference ID, and the hidden text value. After
this, we delete the references older than one day, to prevent them from stacking
up and wasting space.

//Insert reference into database, and delete any old ones
mysql_connect("localhost", "username", "password")
or die(mysql_error());

mysql_select_db("dw_php");
//Create reference
mysql_query("INSERT INTO security_images (insertdate, referenceid, hiddentext)
VALUES (

now(), ‘".$referenceid."’, ‘".$textstr."’)");
//Delete references older than 1 day
mysql_query("DELETE
FROM security_images
WHERE insertdate < date_sub(now(),
interval 1 day)"
);


Finally, we end the output by using exit, and close
the PHP block.

//End Output
exit;

?>

The complete code listing for securityimage.php should
now look like this:

<?php
//Generate Reference ID

if (isset($HTTP_GET_VARS["refid"]) && $HTTP_GET_VARS["refid"]!="")
{

   $referenceid = stripslashes($HTTP_GET_VARS["refid"]);
} else {
   $referenceid =
md5(mktime()*rand());

}

//Select Font
$font = "C:\WINDOWS\Fonts\Century.ttf";

//Select random background image
$bgurl = rand(1, 3);
$im = ImageCreateFromPNG("images/bg".$bgurl.".png");

//Generate the random string
$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","L","m","M","n","N","o","O","p","P","q","Q","r","R","s","S","t","T","u","U","v",
"V"
,"w","W","x","X","y","Y","z","Z","1","2","3","4","5","6","7","8","9");
$length = 8;
$textstr = "";
for ($i=0; $i<$length; $i++)
{

   $textstr .= $chars[rand(0, count($chars)-1)];
}

//Create random size, angle, and dark color
$size = rand(12, 16);
$angle = rand(-5, 5);
$color = ImageColorAllocate($im, rand(0, 100), rand(0, 100),
rand(
0, 100));

//Determine text size, and use dimensions to generate
x & y coordinates

$textsize = imagettfbbox($size, $angle, $font, $textstr);
$twidth = abs($textsize[2]-$textsize[0]);
$theight = abs($textsize[5]-$textsize[3]);
$x = (imagesx($im)/2)-($twidth/2)+(rand(-20, 20));
$y = (imagesy($im))-($theight/2);

//Add text to image
ImageTTFText($im, $size, $angle, $x, $y, $color, $font, $textstr);

//Output PNG Image
header("Content-Type:
image/png"
);
ImagePNG($im);

//Destroy the image to free memory
imagedestroy($im);

//Insert reference into database, and delete any
old ones

mysql_connect("localhost", "username", "password")
or die(mysql_error());

mysql_select_db("dw_php");
//Create reference
mysql_query("INSERT
INTO security_images (insertdate, referenceid, hiddentext) VALUES (

now(), ‘".$referenceid."’,
‘"
.$textstr."’)");
//Delete references older than 1 day
mysql_query("DELETE
FROM security_images
WHERE insertdate < date_sub(now(), interval 1 day)"
);

//End Output
exit;

?>

Open securityimage.php in a browser. You should see
something like the image below:

Now, we are finished with the file and ready
to put it to use!

Note: If you receive a message about ImageCreateFromPNG being
an undefined function, this is probably because you don’t have the GD Graphics
Library installed. Install this (download may be obtained from http://www.boutell.com/gd/)
and retry.

Creating the Sign-up Demo - Signupdemo.php

Now we are ready to create the sign-up demo. To begin, start with the following
blank PHP file:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD
HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>

<meta http-equiv="Content-Type" content="text/html;
charset=iso-8859-1"
>
<title>
Signup Demo</title>
</head>

<body>

</body>
</html>

Now, we need to create two functions in a PHP block at the top of the file -
insertSecurityImage, a function to insert a unique security image, and checkSecurityImage,
a function to check the user entered value.

In insertSecurityImage, we have one parameter, $inputname.
We first of all create a unique reference ID by hashing the current UTC time
times a random number. Then, we generate a string that will insert an image with
the reference ID passed in the querystring. It also inserts a hidden input named
whatever was passed in $inputname, and a value of the
reference ID. Finally, we write it to the browser.

<?php
//Define function to insert security image
function insertSecurityImage($inputname) {
   $refid = md5(mktime()*rand());
   $insertstr = "<img src=\"securityimage.php?refid=".$refid."\" alt=\"Security
Image\">\n
   <input type=\"hidden\" name=\""
.$inputname."\" value=\"".$refid."\">";
   echo($insertstr);
}

In checkSecurityImage, we have two parameters - the
reference ID ($referenceid), and the value the user
entered ($enteredvalue). First, we prepare the two
strings by escaping them for database use. Then, we select the record(s) containing
the reference ID and the hiddentext that were passed to the function. Finally,
we return true (indicating valid) if there are more than zero records, and otherwise
return false (indicating invalid) if there are zero records. You must have already
opened a MySQL connection to use this function.

//Define function to check security image confirmation
function checkSecurityImage($referenceid, $enteredvalue) {
   $referenceid = mysql_escape_string($referenceid);
   $enteredvalue = mysql_escape_string($enteredvalue);
   $tempQuery = mysql_query("SELECT
ID FROM security_images WHERE
   referenceid=’"
.$referenceid."’
AND hiddentext=’"
.$enteredvalue."’");
   if (mysql_num_rows(
$tempQuery)!=0) {
      return
true;
   } else {
      return
false;
   }
}

?>

Now, we are ready to create the signup form within the <body> tags. The form
will post to itself. Because this is a demonstration, we will only have one field
(name)
in addition to the security image. Then, we simply call the insertSecurityImage function,
specifying the hidden form field name to be security_refid. Finally
we add a confirmation field named security_try, and
a submit button.

<form name="signupform" method="post" action="<?=$_SERVER["PHP_SELF"]?>">
Please sign up for our website:
<br>

<br>
Name:
<input name="name" type="text" id="name">
<br>
<? insertSecurityImage("security_refid") ?>
<br>
Enter what you see:
<input name="security_try" type="text" id="security_try" size="20" maxlength="10">
(can’t see? try reloading page)
<br>
<br>

<input type="submit" name="Submit" value="Signup!">
</form>

Open signupdemo.php in a browser. If you view the source, you should see something
like the code listing below. Note the hidden field and the security image reference
ID.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Signup Demo</title>
</head>

<body>
<form name="signupform" method="post" action="/php/signupdemo.php">
Please sign up for our website:<br>
<br>
Name:
<input name="name" type="text" id="name">
<br>
<img src="securityimage.php?refid=0ed393101d240092186f47c3dc80c84e" alt="Security
Image">

<input type="hidden" name="security_refid" value="0ed393101d240092186f47c3dc80c84e"> <br>
Enter what you see:
<input name="security_try" type="text" id="security_try" size="20" maxlength="10">
(can’t see? try reloading page)
<br>
<br>
<input type="submit" name="Submit" value="Signup!">
</form>
</body>
</html>

We are now ready to write the form handler script. We will be adding another
PHP block just above the <form> tag. First of
all, we check to see if the security_refid and name fields
are defined. If so, we connect to the MySQL database. Be sure to enter your
username and password. Next, we define $security_refid and $security_try based
on the form values. After that, we call checkSecurityImage using
the reference ID and the value the user entered. Based on the value returned,
we tell the user if they entered the correct code or not.

<?php
if (isset($HTTP_POST_VARS["name"]) && isset($HTTP_POST_VARS["security_try"]))
{
  
//Connect to database
   mysql_connect(
"localhost", "username", "password");
   mysql_select_db(
"dw_php");
  
//Set variables, and call checkSecurityImage
   $security_refid
= $HTTP_POST_VARS["security_refid"];
   $security_try =
$HTTP_POST_VARS["security_try"];
   $checkSecurity = checkSecurityImage($security_refid, $security_try);
   //Depending on result, tell user entered value was correct or incorrect
   if (
$checkSecurity) {
      $validnot = "correct";
   } else {
      $validnot = "incorrect";
   }
   //Write output
   echo("<b>You entered this as the security text:</b><br>\n
   "
.$security_try."<br>\n
   This is "
.$validnot.".<br>\n
   ——————————-<br><br>\n
   "
);
}
?>


The complete code listing for signupdemo.php should now look like this:

<?php
//Define function to insert security image
function insertSecurityImage($inputname)
{

   $refid = md5(mktime()*rand());
   $insertstr = "<img
src=\"securityimage.php?refid="
.$refid."\" alt=\"Security
Image\">\n
   <input type=\"hidden\" name=\""
.$inputname."\" value=\"".$refid."\">";
   echo($insertstr);
}

//Define function to check security image confirmation
function checkSecurityImage($referenceid, $enteredvalue)
{

   $referenceid = mysql_escape_string($referenceid);
   $enteredvalue = mysql_escape_string($enteredvalue);
   $tempQuery = mysql_query("SELECT
ID FROM security_images WHERE
   referenceid=’"
.$referenceid."’
AND hiddentext=’"
.$enteredvalue."’");
   if (mysql_num_rows(
$tempQuery)!=0)
{
      return
true;
   } else {
      return
false;
   }
}

?>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD
HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>

<meta http-equiv="Content-Type" content="text/html;
charset=iso-8859-1"
>
<title>
Signup Demo</title>
</head>

<body>

<?php
if (isset($HTTP_POST_VARS["name"]) && isset($HTTP_POST_VARS["security_try"]))
{
  
//Connect to database
   mysql_connect(
"localhost", "username", "password");
   mysql_select_db(
"dw_php");
  
//Set variables, and call checkSecurityImage
   $security_refid
= $HTTP_POST_VARS["security_refid"];
   $security_try = $HTTP_POST_VARS["security_try"];
   $checkSecurity = checkSecurityImage($security_refid, $security_try);
   //Depending on result, tell user entered
value was correct or incorrect

   if (
$checkSecurity)
{

      $validnot = "correct";
   } else {
      $validnot = "incorrect";
   }
   //Write output
   echo("<b>You
entered this as the security text:</b><br>\n
   "
.$security_try."<br>\n
   This is "
.$validnot.".<br>\n
   ——————————-<br><br>\n
   "
);
}
?>

<form name="signupform" method="post" action="<?=$_SERVER["PHP_SELF"]?>">
Please sign up for our website:
<br>
<br>
Name:
<input name="name" type="text" id="name">
<br>
<? insertSecurityImage("security_refid") ?>
<br>
Enter what you see:
<input name="security_try" type="text" id="security_try" size="20" maxlength="10">
(can’t see? try reloading page)
<br>
<br>
<input type="submit" name="Submit" value="Signup!">

</body>
</html>

Preview signupdemo.php in a browser. You should see something like this:

If you enter the text correctly and submit, you should see something like this:

Note: if you receive a MySQL error, ensure that you replaced "username" and "password"
with the correct values.

Summary and Conclusion

In this article we have learned how to use the GD Graphics Library, creating
an easy solution to prevent computers from behaving like humans. We
also wrote reusable functions, allowing us to use the Security Image creator
file in other applications.

Until next
time, happy coding :-)