View Full Version : Problems with creating a CAPTCHA image

10-31-2007, 09:54 PM
So our site is getting bombarded by spammers using our contact forms to contact our agents. My boss wanted me to try and stop it. I have a CAPTCHA image working on my desktop(WINXP running Apache with PHP Version 5.2.4 w/ gd 2.0.34) where the image works fine. When I upload it to either of our two actual live servers(PHP Version 5.1.6 w/ gd 2.0.28 and PHP Version 4.3.9 w/ gd 2.0.28), there comes 1 of 3 problems.

How the image looks on my desktop:

How the PHP 4.3.9 box displays it:

How the PHP 5.1.6 box displays it:

Now, admittedly, this is my first time working with creating images so I am not really sure what to look for. Is the discrepancy in the version differences on the live servers compared to my desktop test server? Is there something in my code that I am missing?


$width = 400;
$height = 400;

function rotateAndCropImage($editImage, $width, $height){
$editImage = imagecreatefrompng($editImage);
imagesavealpha($editImage, false);
imagealphablending($editImage, true);
$degrees = rand(0, 4);
$degrees = $degrees * 90;
$editImage = imageRotate($editImage, $degrees, 0);
$eWidth = imagesx($editImage);
$eHeight = imagesy($editImage);
$eWidth = $eWidth - ($eWidth/2);
$eHeight = $eHeight - ($eHeight/2);
$xPosition = rand(0, $eWidth);
$yPosition = rand(0, $eHeight);
$cropImage = imagecreate($width,$height);
imagesavealpha($cropImage, false);
imagecopyresized($cropImage, $editImage, 0,0, 0,0, imagesx($editImage), imagesy($editImage), imagesx($editImage), imagesy($editImage));
return $cropImage;

$image = imagecreatetruecolor($width, $height);
imagesavealpha($image, false);
imagealphablending($image, true);

$backgroundImage = "bg.png";
$backgroundImage = rotateAndCropImage($backgroundImage, $width, $height);
imagecopy($image, $backgroundImage, 0,0,0,0,$width,$height);

$image1 = "1.png";
$image2 = "2.png";
$image3 = "3.png";
$image4 = "4.png";
$image5 = "5.png";
$image1 = rotateAndCropImage($image1, $width, $height);
imagecopy($image, $image1, 0,0,0,0,$width,$height);
$image2 = rotateAndCropImage($image2, $width, $height);
imagecopy($image, $image2, 0,0,0,0,$width,$height);
$image3 = rotateAndCropImage($image3, $width, $height);
imagecopy($image, $image3, 0,0,0,0,$width,$height);
$image4 = rotateAndCropImage($image4, $width, $height);
imagecopy($image, $image4, 0,0,0,0,$width,$height);

$totalChar = 8;
$salt = "abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ123456789";
for ($i=0;$i<$totalChar;$i++){
$letter = substr ($salt, rand() % strlen($salt), 1);
$captcha = $captcha . $letter;
$transparency = rand(0,25);
$randColor = rand(0,100);
$textColor1 = imagecolorallocatealpha($image, $randColor,$randColor,$randColor, 0);
$randColor = rand(250, 255);
$textColor2 = imagecolorallocatealpha($image, $randColor,$randColor,$randColor, $transparency);
$fontSize = rand(25,40);
$angle = rand(-20,20);
$textX = 55 + $i*27;
$textY = rand(137, 140);
$font = "" . rand(1,4) . ".ttf";
imagettftext($image, $fontSize+2, $angle, $textX-1, $textY+1, $textColor1, $font, $letter);
imagettftext($image, $fontSize, $angle, $textX, $textY, $textColor2, $font, $letter);

$image5 = rotateAndCropImage($image5, $width, $height);
imagecopy($image, $image5, 0,0,0,0,$width,$height);

$finalImage = imagecreatetruecolor(225, 50);
imagesavealpha($finalImage, false);
imagecopy($finalImage, $image, 1,1, 50, 100, 223, 48);

header("Content-Type: image/jpeg");


Thanks for any help.

P.S. this is definitely not the final version. I still have to go back and edit my PNGs down a little bit in size and get rid of the whole cropping issue. And if anyone has any ideas on how to handle the character spacing, my ears are open.

Thanks again.

10-31-2007, 10:12 PM
Check your web server log for errors that might be occurring due to functions or features that are present on one system and not another.

It is often more effective to close the loop holes in the form processing code that is allowing the spam content to be sent, rather than burden a user with trying to decode and enter a CAPTCHA image. Note: The copy of the spam message you are receiving is probably just due to the To: address field in the email, if your code is allowing mail header injection, the same spam is probably being sent to any number of other people through your mail server.

By validating the input from the form fields to make sure they only contain content that you want, you can eliminate the sending of the spam without needing to resort to a CAPTCHA. Even with a CAPTCHA, someone can still send the spam if your form processing code allows it, if they are willing or able to manually or automatically read the CAPTCHA value.

Also, image CAPTCHA's are fairly easy to automatically decode - http://www.ocr-research.org.ua/index.html

10-31-2007, 11:31 PM
There are no errors spitting back on the images when they are created. Could it be the version differences in GD?

I do agree that closing the loopholes is more user friendly than CAPTCHA images. Currently the forms check for a properly formatted email address, first and last name, and a message. The spam that is being sent meets ALL of those requirements.

The new form I am working on will keep track of the IP of the sender and the time in a table. It will then check against that table of IPs and if that IP has sent more than 3 messages in the past x minutes, it will "IP ban" them from the form and just hide it from their view. If this happens 3 times total, I will have it notify me so I can yadda yadda yadda.

If that form does not solve the problem, I want the CAPTCHA as a backup plan that I can easily roll out into place to further deter efforts. And also, I've never had a reason to create dynamic PHP images, so this is fun to me :)

10-31-2007, 11:40 PM
Just remember that the things that allow a human to read your image, are also the things that allow a computer to read your image.

For example, all your colourful noise? All I have to do is remove any non-white pixel and I have clean data that I can now scan using OCR. And if you think the rotating/skewing will make it a lot harder... There are some really good character recognizers out there.

Not to ignore the fact that people get hired to read these images.

10-31-2007, 11:49 PM
If you were to post your form processing code, I bet we can either point out holes in it's checking or provide ways of closing those holes.

I did not spend any time looking at your actual images, but aedrin is correct. All the letters are shades of one color. By simply removing all the other colors, you would be left with just the letters. The OCR link in my post above contains a lot of useful information about what makes a bad and a good CAPTCHA image.

A question/answer form of a CAPTCHA (what is 2 x 5 for example), using a fairly large number of random questions, is currently proving more effective than image CAPTCHA's.

11-01-2007, 12:09 AM
Perhaps I should start over from the beginning.

The forms we have on the site are basic mail forms. A prospective client sees a home, fills in the form, hits submit. The values are checked for a valid looking email address, first name, last name, etc. The email is generated and sent to a program called leadrouter. Leadrouter further scrubs. It again checks for a valid looking email address before continuing. From there leadrouter calls the agent(based on the MLS number of the property) and puts them in contact with the client.

We aren't getting blank emails, so no problems there. We get emails that have VALID looking email addresses and contact information that go NOWHERE. These messages usually happen in bundles of 10-15 at a time, hence the count I will be implementing with the "IP Ban". I am hoping that this will slow or stop the problem.

Now enough about the forms. The question is not on the forms or their processing. The question is on the PHP and GD Library and possible differences between the three servers that we are running. Where should I start the searching process, since there are no errors being reported? What is the best way to trouble shoot images?

And I am well aware of the cons to using CAPTCHA images. I've been reading for the past few days on ways to stop the problems that we are having and I PROMISE I will start a new thread later on the form processing if I cannot come to a viable solution on how to solve that particular problem. But again, this is not about the forms, this is about the GD library.

P.S. I don't mean GD in a Archie Bunker kind of way.

11-01-2007, 05:39 AM
Someone found a way of getting humans to read and enter CAPTCHA phrases from a site (just grab the image from the site you want to post to, display it on your page with an input form, when someone enters the answer, just post it back to the actual site using your script, repeat as necessary with the next image) where they can then use the entered phrase to post content or register for email addresses or register on a site... - http://www.msnbc.msn.com/id/21566341/

There are also several techniques that you can add to forms and form processing code to insure that it is your form that is posting to your form processing code, including but not limited to -

Start a session and set a session variable to some arbitrary value in the form code and check in the form processing code for that session variable with that value (destroy the value or session after checking it to prevent reuse.)

Put a hidden dummy form first on your page and let scripts find it. Most scripts don't check for more than one form.

Put a hidden form field that contains a unique random value (passed in another session variable for checking in the form processing code) that must be present in the submitted data - catches scripts that don't copy fields or that overwrite all fields with their spam content.

11-01-2007, 03:36 PM
Again, the question right now is not on the forms. I appreciate all of those comments though. Really. Although everything you have already said has been worked into the forms, but perhaps I am missing something.

Right now, the question is on creating images. The "black" problem comes in when I use imagerotate() on an image and it rotates it to 90 or 270. Any ideas on how to solve this.

And I'm serious, I will post up the forms by the end of the day today and let you look at them if you can just stay on the GD topic :)

Again, GD is not meant in an Archie Bunker kind of way.

11-01-2007, 10:23 PM
If this gets the answer to the GD question, here is the form


if($submitSession == $_POST['sid']){
$error = array();
foreach($_POST as $formInput){
if(eregi("/(%0A|%0D|\n+|\r+)(content-type:|to:|cc:|bcc:)/i", $formInput)){
$error['other'] = "There was a problem with the information you provided. Please try again.";
if(eregi("/(%0A|%0D|\n+|\r+)/i", $formInput)){
$error['other'] = "There was a problem with the information you provided. Please try again.";
if(@ $_POST['firstname'] == ""){
$error['firstname'] = "Please enter a valid first name.";
if(@ $_POST['lastname'] == ""){
$error['lastname'] = "Please enter a valid last name.";
if(@ strlen($_POST['contactphone']) < 10){
$error['contactphone'] = "Please enter a valid telephone number.";
if(count($error) < 1){
$header = "From: kpdk@kpdk.com\r\n";
$header .= "X-Sender: kpdk@kpdk.com \r\n";
$header .= "Return-Path: kpdk@kpdk.com \r\n";
$message .= "MLS#: $mls\r\n";
$message .= "Name: " . $_POST['firstname'] . " " . $_POST['lastname'] . "\r\n";
$message .= "Email: " . $_POST['contactemail'] . "\r\n";
$message .= "Phone: " . $_POST['contactphone'] . "\r\n";
$message .= "Comments: " . $_POST['comments'] . "\r\n";
$lrmail = mail("147061.lead@cendant.leadrouter.com", $message, $header) or $error['other'] = "Email could not be sent. Please check your information again.";
$noerror = "1";
echo "<div style=\"border: 1px solid black; padding: 5px; background-color: #acf; text-align: center; color: red;\">Your message has been sent</div><br />";
echo "<form name=\"leadRouter\" method=\"post\" action=\"\" style=\"border: 1px solid black; padding: 5px; background-color: #acf; text-align: center;\" >";
echo "<p>Please use the form below if you would like more information on the property or to set up an appointment to view this property.</p>";
foreach($error as $reason){
echo "<span style=\"color: red;\">$reason</span><br />\r\n";
echo "<input type=\"hidden\" name=\"mls\" value=\"$mls\" />";
echo "<input type=\"hidden\" name=\"sid\" value=\"";
echo session_id();
echo "\" />";
echo "<span style=\"width: 100px; text-align: left;\">First Name:</span><input name=\"firstname\" size=\"35\" value=\"" . @ $_POST['firstname'] . "\" /><br />";
echo "<span style=\"width: 100px; text-align: left;\">Last Name:</span><input name=\"lastname\" size=\"35\" value=\"" . @ $_POST['lastname'] . "\" /><br />";
echo "<span style=\"width: 100px; text-align: left;\">Email:</span><input name=\"contactemail\" size=\"35\" value=\"" . @ $_POST['contactemail'] . "\" /><br />";
echo "<span style=\"width: 100px; text-align: left;\">Phone:</span><input name=\"contactphone\" size=\"35\" value=\"" . @ $_POST['contactphone'] . "\" /><br />";
echo "<span style=\"text-align: left;\">Additional comments:</span><br />";
echo "<textarea name=\"comments\" rows=\"8\" cols=\"62\"></textarea><br />";
echo "<input type=\"submit\" name=\"submit\" value=\"submit\" /><input type=\"reset\" value=\"cancel\" />";
echo "</form>";


11-01-2007, 10:39 PM
I'll be the first to admit I'm out of my depth on this one. But I did notice a note on the imagettftext() (http://us3.php.net/manual/en/function.imagettftext.php) function that mentioned you must have the GD library AND the FreeType library installed. Might the FreeType library not be installed on the live server?

And then the other debugging process is to break your script down to basics and figure out which of the gazillion steps it goes through to create the image is causing the meltdown.

11-01-2007, 10:41 PM
Or, you can try something like this:


But I totally understand the drive to make something work even if there may be other ways to achieve the same thing.

11-01-2007, 10:48 PM
EXACTLY! I will probably NEVER use the CAPTCHA on the site, but if I figure out what is causing the problem, this could save time on a future project down the road. :)

I've been playing with it off and on today. If I limit the image to the background, text and top splatter layer, it will USUALLY output fine. There are still times where it outputs with just the top splatter layer. At first I thought it was based on the rotation of the images, so I commented out that line. It would yeild a "complete" picture 90% of the time and other times it would just display that top splatter layer. I'm thinking it may be memory allocation, but haven't had a chance to do any reading on that today. Probably give that a look tonight.

11-01-2007, 10:52 PM
For any differences between systems - http://php.net/gd_info

The description of imagerotate() includes a note that it is only available when php is complied with bundled versions of the GD library, perhaps on your php4.3.9 system it was added separately - http://php.net/imagerotate

As a side note, the end of life of php4 is the end of this year, in two months. The system with php4 will need to be upgraded sooner rather than later or get left behind.

11-01-2007, 10:56 PM
The server with PHP4 is hosted by bellsouth/att and will hopefully DIAF once I transfer everything to our new server this month. :)

11-01-2007, 10:57 PM
There are no errors spitting back on the images when they are created.Errors are output as text. They won't appear on the images (edit: unless you specifically detect them in your code and put them as text onto the image.)

Did you ever check your web server log for errors as had been suggested?

11-01-2007, 11:12 PM
I have it set to log errors on all 3 servers and the error logs are clean as a whistle.

11-02-2007, 06:03 PM
well, not the memory. I upped memory_limit on the live servers to match my desktop and still spitting out the same thing. Error logs are still clean.

11-02-2007, 10:19 PM
An error that occurs only 10% of the time indicates your machine is haunted by evil spirits of coding past. These spectres are basically screwing with you.

(sorry for the tongue in cheek)

(I wish I could be helpful)

11-06-2007, 05:29 PM
I wrote an exorcism script, but can't get permission from the vatican to run it on the live server.