Last November, I published a post on creating a simple PHP Captcha. Some readers noted that I actually used a more advanced captcha for my own site’s discussion pages, and asked for the source code. I’ve actually gone a step further and improved my own captcha by adding more background noise, decreasing the likelihood that a bot would be able to break it. Though harder for a bot to read, I changed the font and made it easier for a human to read.
As noted before, Captchas are images that are meant to tell the difference between a person and a computer, be presenting an image with text in it, and asking the user to enter the text and submit it with the form. Captchas are used to reduce spam on publicly accessible message boards and comment lists.
Below is the new and improved PHP captcha script and example implementation.
Requirements
To use this script, you will need PHP enabled web server with the Image (GD) extension enabled. If you are not sure, check with your hosting company. Most major hosting companies, like GoDaddy.com provide this by default.
Setup
On the page where your users are submitting content, like a post or a comment, you want to display the captcha and an input box for the user to enter the text displayed in the captcha. To use the PHP script below to you need to include it as an image on the page you want it to display - save the script as something like “advanced_captcha_script.php” and use the following html to display it on the page.
Include this html code inside your form tag:
<!-- display the script as an image --> <img src="advanced_captcha_script.php" /> <!-- an input box to input the captcha text --> <input name="captcha_input" id="captcha_input" />
The Script
<?php session_start(); //MUST START SESSION $string_length = 6; //NUMBER OF CHARS TO DISPLAY $large_letters = array('m','w'); $rand_string = ''; for ($i=0; $i<$string_length; $i++) { //PICK A RANDOM LOWERCASE LETTER USING ASCII CODE $rand_string .= chr(rand(97,122)); } //IMAGE VARIABLES $width = 100; $height = 36; //INIT IMAGE $img = imagecreatetruecolor($width, $height); //ALLOCATE COLORS $black = imagecolorallocate($img, 0, 0, 0); $gray = imagecolorallocate($img, 110, 110, 110); $medgray = imagecolorallocate($img, 180, 180, 180); $lightgray = imagecolorallocate($img, 220, 220, 220); //FILL BACKGROUND imagefilledrectangle($img, 0, 0, $width, $height, $lightgray); //ADD NOISE - DRAW background squares $square_count = 6; for ($i = 0; $i < 10; $i++) { $cx = (int)rand(0, $width/2); $cy = (int)rand(0, $height); $h = $cy + (int)rand(0, $height/5); $w = $cx + (int)rand($width/3, $width); imagefilledrectangle($img, $cx, $cy, $w, $h, $medgray); } //ADD NOISE - DRAW ELLIPSES $ellipse_count = 5; for ($i = 0; $i < $ellipse_count; $i++) { $cx = (int)rand(-1*($width/2), $width + ($width/2)); $cy = (int)rand(-1*($height/2), $height + ($height/2)); $h = (int)rand($height/2, 2*$height); $w = (int)rand($width/2, 2*$width); imageellipse($img, $cx, $cy, $w, $h, $gray); } //REPLACE THIS WITH THE FONT YOU UPLOAD $font = 'Tuffy.ttf'; $font_size = 18; //CALC APPROX LOCATION - CUSTOMIZED FOR ABOVE FONT $y_value = ($height/2) + ($font_size/2); $x_value = 0; //DRAW STRING USING TRUE TYPE FUNCTION for ($i = 0; $i < $string_length; $i++) { $chr = substr($rand_string, $i, 1); $x_value += 3 * ($font_size/5); imagettftext($img, $font_size, 0, $x_value, $y_value, $black, $font, $chr); //check to see if larger than normal letters, if so add more horiz space if (in_array($chr, $large_letters)) { $x_value += 4; } } $_SESSION['encoded_captcha'] = md5($rand_string . 'my_secret_key'); //OUTPUT IMAGE HEADER AND SEND TO BROWSER header("Content-Type: image/png"); imagepng($img); ?>
Here’s an example of what the captcha looks like (refresh the page to generate a new captcha):
Script Notes
We are using a font called “Tuffy.ttf” that is in the public domain in this script. You will need to download this font and upload to the same directory as the captcha script. You can download this font from the homepage of its creator or directly from my site.
Checking The Result
After the user has submitted the form, we still have to validate their captcha text entry. Without validation, our captcha security is useless. The general flow of things after the form has been submitted is:
- Compare the user entry to the captcha text
- If they are the same, enter the user’s content into your database
- If they are different, display an error message to the user.
If you examine the PHP script above you will notice that we are storing the generated captcha as a session variable concatenated with a secret key. If we use the PHP function session_start() on the next page after the form has been submitted, we will be able to access that stored session variable. We do not store it as plain text, or else a spam bot might be able to access the stored text in the session variable and then type the correct captcha text in the input box, bypassing our security. Instead, we store it a an md5() hash, which is a one-way encryption method. All md5() encrypted strings by their nature cannot be decrypted, so to validate the user entry we need to md5() encrypt the user’s input, concatenate in the secret key and compare it to the already encrypted session variable. Below is some PHP code that shows how we could validate the captcha:
<?php session_start(); $user_content=trim($_POST['user_content']); $user_captcha_input=trim($_POST['user_captcha_input']); if (isset($_SESSION['encoded_captcha'])) { if ($_SESSION['encoded_captcha'] == md5($user_captcha_input . 'my_secret_key')) { //..THE USER IS NOT A BOT, STORE THEIR INPUT } else { //THE USER ENTERED THE WRONG CAPTCHA, //DISPLAY AN ERROR MESSAGE AND //DO NOT STORE THEIR INPUT } } ?>

