Security Images with PHP and ImageMagick

This article is intended to provide another look at Nathan Rohler’s article that was recently published (the link is provided at the end of this article). His article illustrated how to use a database and PHP’s image functions based on the GD library to create random security images. This article will demonstrate how to achieve the same goal (albeit with slightly different results) by using ImageMagick. I chose not to use hidden form fields and a database and instead opted for session variables, a much simpler and more efficient approach for our objective.

Getting Started

I chose to write this article using command line calls to the ImageMagick utility “convert”. Most PHP users are developing websites on servers with virtual hosting packages and may not have the ability to use PHP with GD or ImageMagick support compiled in. While it is also possible that ImageMagick may not be available, it is highly unlikely that neither ImageMagick nor GD will be available to PHP users.

For those who can freely install software on their server, and for those who want to find out if they can use this code, there are a few pieces of software that must be installed. The usage of ImageMagick in this article requires that LIBPNG and TrueType are installed and that ImageMagick was compiled with support for them. If ImageMagick is already installed on your server, it is highly likely that it is set up in this manner. The plus to this is that if neither the GD nor the ImageMagick option is available to you, administrators are far more likely to install a utility like ImageMagick than to rebuild PHP on their server hosting hundreds of virtual sites to support GD.

There are a few differences in our capabilities when using ImageMagick vs using GD, the most noticeable of which has to do with handling fonts and text. GD is able to create a truetype textbox and return various properties about that box based on the font face and size assigned to it. In ImageMagick, there is no comparable feature. This limits our ability to position text on our background randomly. The problem is being unable to calculate how much space your text will occupy in a given font with a given size because ImageMagick only accepts font size in points. If you are familiar with typography, you will know that point size lends itself to unpredictable and arbitrary results on the web. If you choose to use a font other than the one I use in this article, you will most likely have to tinker with the range of text sizes to use.

{mospagebreak title=The Image Generator}

First, let me quickly go over the files and directories involved with our little application. They are available in the support files for this article.

/images/tmp/ (chmod to 777)

We will use Arial as our font of choice in this article, and we will use the three background images from the original article. Take note of the /images/tmp/ directory. This is where our random images will be stored. Since we are not creating and manipulating an image in memory as we would be with GD through a PHP script, we have to save out an actual copy of our generated security image, and the tmp directory is where they’ll go.

Let’s have a look at the first portion of our securityimage.php script:


$bgdir = ‘images/’;
$bgs = array(‘bg1.png’, ‘bg2.png’, ‘bg3.png’);
$im = $bgdir.$bgs[rand(0, count($bgs)-1)];

I typically use output buffering in all my scripts to avoid random problems regarding setting cookies or session variables after outputting to the browser, so we begin with ob_start(), then of course session_start() to initiate our session. The next bit of code simply selects a random background image. We simply pick a random image from an array of predefined images and store the value. The images could also be kept track of in a database or we could manually browse the directory on each run and grab an image, but realistically you will not need more than a few backgrounds, so I think this approach is fine.


$chars = array(‘a’,'A’,'b’,'B’,'c’,'C’,'d’,'D’,'e’,'E’,'f’,'F’,'g’,'G’,




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

$hashtext = md5($textstr);
$tmpname = $hashtext.’.png’;
$_SESSION['strSec'] = $hashtext;

First off we define the characters that we want to use in our random string, then we go through a for loop of eight iterations, each time adding one random character from our array to our random string variable. Once that is done, we hash the text with md5() and store it in a session variable. We will use this session variable to check that the user enters the correct value in the signup form. We also define $tmpname which will house the name of the image we will create in the tmp directory.

$font = ‘arial.ttf’;
$size = rand(24, 36);
$hexValues = array(’0′,’1′,’2′,’3′,’4′);
$numHex = count($hexValues);
$color = ”;
for ($i = 0; $i < 6; $i++) {
 $color .= $hexValues[rand(0, $numHex-1)];
$gravities = array(‘West’, ‘Center’, ‘East’);
$gravity = $gravities[rand(0, count($gravities)-1)];

$angle = rand(-5, 5);

This chunk of code decides how our text will look. First we pick a font (remember, you can use any font you like, but different fonts have different sizes and kerning so they will require experimentation to achieve proper positioning), then we generate a random hex color using only 0, 1, 2, 3 and 4 as possible values to ensure that our color is dark enough to be visible on our background. Next we define the gravities that we want to use.

Gravity in ImageMagick allows us to decide how we want primitives (simple shapes and text) to gravitate in our image. I chose to use the gravities that would cause the fewest problems getting text to appear without clipping.

After that we randomly select a gravity then we randomly select an angle between -5 and 5 degrees. Something important to note here is that our angle will actually tilt the whole image, background and all, not just the text. While we could achieve tilting just the text, it would require multiple ImageMagick commands as well as multiple temporary images and some image compositing. Tilting the image as a whole still gives us the randomness that we need to avoid screen scrapers, and that’s what matters.

$cmd  = ‘/usr/bin/convert’;
$cmd .= ‘ -font “‘.$font.’”‘;
$cmd .= ‘ -fill “#’.$color.’”‘;
$cmd .= ‘ -pointsize ‘.$size;
$cmd .= ‘ -gravity “‘.$gravity.’”‘;
$cmd .= ‘ -draw ‘text 0,0 “‘.$textstr.’””;
$cmd .= ‘ -rotate ‘.$angle;
$cmd .= ‘ ./’.$im.’ ./images/tmp/’.$tmpname;


This bit of code is where we actually prepare the ImageMagick command using the convert utility. I have broken up the switches by line for readability. You may have to change the path to convert depending on your system (using `which convert` from a shell will tell you where it’s at).

The first line is where we set our font with the -font switch. The next line is where we define our fill color. If you are familiar with drawing tools like Photoshop or Flash, you will know that fill applies to anything drawn on your canvas using tools that work with your fill color. Line three defines the size of the text in points (72 points per inch in typography but it can vary on computers) with the -pointsize option.

Next we define gravity with the -gravity switch. This ensures that our text will align left, right, or center using the $gravity variable defined above. The next line, -draw, is the meat of our call to convert. Draw allows us to create primitives such as circles, squares, and in this case text. The keyword “text” lets draw know that we will be drawing a text primitive.

Following this are the x,y coordinates to use when drawing our text. Since we are using gravity for positioning, these are left at 0,0. After that we simply plug in our random text from above to complete the -draw option. The following line tells convert to rotate our image by our randomly chosen angle, and the last line specifies our randomly chosen background image as the source and the name we chose as a temporary file in the tmp directory as the target. This is topped off with a call to exec() to run the command.

header(‘Content-Type: image/png’);
print fread(fopen(‘./images/tmp/’.$tmpname, ‘r’), filesize(‘./images/tmp/’.$tmpname));

exec(‘rm -f ./images/tmp/’.$tmpname);


The last portion of the securityimage.php script is what actually sends the image back to the browser. We first send an HTTP Content-Type header, then we echo the contents of our temporary image file. After we have read and outputted the image, we have no more use for the file itself, so we delete it with a command line call. This keeps our temp directory nice and neat. Lastly, we call ob_end_flush(); to send our data to the browser.

{mospagebreak title=The Form}

Our form is much simpler and has very little PHP code in it. It starts out quite simply and in similar fashion to the securityimage.php script except that this page is sending HTML output to the browser so we have the usual HTML up top.


<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” ““>
<html xmlns=”“>
<title>Secure Images Demo</title>
<meta http-equiv=”Content-Type” content=”text/html; charset=iso-8859-1″ />

Once that’s done, we can define our form function. Putting the form in a function allows us to call it selectively rather than showing the form again even after successful submittal (as was the case in the original article). The function is simple and contains a minimal form:

function Form() {
 <form method=”post”>
 Please sign up for our website:<br /><br />
 Your Name: <input name=”name” type=”text” size=”25″ /> <br /><br />
 <img alt=”Security Image” src=”securityimage.php” /><br/ ><br/ >
 Enter what you see: <input name=”securityImageValue” type=”text” size=”15″ /><br /><br />
 <input type=”submit” name=”Submit” value=”Signup!” />

The most notable part of the form itself is that the image points directly to the securityimage.php script. Remember, the securityimage.php script sends an image directly back to the browser, no HTML, so this works just fine. Next we will define our form handler.

if (isset($_POST['securityImageValue']) && isset($_SESSION['strSec'])) {
 if (md5($_POST['securityImageValue']) == $_SESSION['strSec']) {
  print ‘You correctly enetered the security image text. Goody for you.’;
 else {
  print ‘The text you entered does not match the security image you saw. Please try again.<br /><br />’;

This bit of code tests to see if the form has been submitted by checking to see if we have a security string hash in our session and if we have submitted a guess at the contents of the security string. If those two conditions prove to be false, we show the form. If they are met, we process the form. We take the md5 hash of the value entered by our user and compare it to the value we stored in their session. If they match, then the user entered the text from the security string correctly. If they do not match, then the wrong text was entered and we need to tell the user and show the form again. This could be made more sophisticated by only allowing a certain number of attempts by a user, but is not necessary, as the security image itself prevents automated signup on it’s own. The last few lines of code just close our HTML and flush our output buffer.




{mospagebreak title=Conclusion}

Hopefully this article demonstrated a more efficient way to handle the security image problem than the original by avoiding the use of a database and hidden form fields. A feature to be added would be a “bad word” filter. Content filtering is beyond the scope of this article, though, and if you feel that you need that feature, there are many open source PHP site engines available that have content filters built in that you could borrow from.

I also used PHP4 code to reflect more up-to-date coding practices and used ImageMagick through the command line to offer another method of creating the security image. You could easily replace the ImageMagick specific code with the GD specific code from the original article and use the method I explained in this article to generate security images using GD.

When troubleshooting this code, be sure that you have set permissions on the temp directory to 777 and that you have the correct path to convert in the command that is executed. Also be sure you have the right path to the font you wish to use and be aware that if you use a different font, you will surely have to tinker with the sizes of the text to make sure it is not too large or too small for the background.

Nathan Rohler’s article can be accesssed at this link.

Google+ Comments

Google+ Comments