HomePHP Page 2 - Security Images with PHP and ImageMagick
The Image Generator - PHP
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.
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.
/arial.ttf /securityimage.php /signupdemo.php /images/bg1.png /images/bg2.png /images/bg3.png /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:
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.
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.
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.
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.
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.