Making a Simple PHP Captcha that Works
Wednesday, February 7th, 2007… Learning is a life long process, if you stop, life stops …
The Problem: I have an email form on my Website, and keep getting these spams “to make my love life better”. While that might be a good intension, I rather not receiving those emails. So, I decided to put a captcha to reduce the number of spams.
I have found bits and pieces on the Web on how to generate a captcha but most of them don’t present a total solution. I will put them all together and hope that this will ease the process of learning.
The Solution: I have this captcha program that generates an image and set a cookie. Then I wrote a html form and a php program that uses this captcha. We first create a php program (captcha.php) that generates the image and set the cookie:
001 <?php
002 function getR($col) {return ( ( $col >> 8 ) >> 8 ) % 256;}
003 function getG($col) {return ( $col >> 8 ) % 256;}
004 function getB($col) {return $col % 256;}
005
006 function create_image() {
007 $bcol = array(0,0,0); // background color
008 $fcol = array(255,228,250); // foreground color
009 $words = array('apple','badge','black'); // words for captcha
010 $fact = 2;
011 $font = 5;
012 $cosrate = rand(10,19);
013 $sinrate = rand(10,18);
014 $charwidth = imagefontwidth($font);
015 $charheight = imagefontheight($font);
016 $pass = $words[rand(0,sizeof($words)-1)];
017 $strlen = strlen($pass);
018 $width = ($strlen + 2) * $charwidth;
019 $height = 2 * $charheight;
020
021 session_start();
022 $_SESSION["pass"] = $pass;
023
024 $im = @imagecreatetruecolor($width, $height)
025 or die("Cannot Initialize new GD image stream");
026 $im2 = imagecreatetruecolor($width*$fact, $height*$fact);
027 $bcol = imagecolorallocate($im, $bcol[0], $bcol[1], $bcol[2]);
028 $fcol = imagecolorallocate($im, $fcol[0], $fcol[1], $fcol[2]);
029
030 imagefill($im, 0, 0, $bcol);
031 imagefill($im2, 0, 0, $bcol);
032 $dotcol = imagecolorallocate($im, (abs(getR($fcol)-getR($bcol)))/2.5,
033 (abs(getG($fcol)-getG($bcol)))/2.5,
034 (abs(getB($fcol)-getB($bcol)))/2.5);
035 $dotcol2 = imagecolorallocate($im, (abs(getR($fcol)-getR($bcol)))/1.5,
036 (abs(getG($fcol)-getG($bcol)))/1.5,
037 (abs(getB($fcol)-getB($bcol)))/3.5);
038 $linecol = imagecolorallocate($im, (abs(getR($fcol)-getR($bcol)))/2.4,
039 (abs(getG($fcol)-getG($bcol)))/2.1,
040 (abs(getB($fcol)-getB($bcol)))/2.5);
041
042 for($i=0; $i<$width; $i=$i+rand(1,5)) {
043 for($j=0; $j<$height; $j=$j+rand(1,5)) {
044 imagesetpixel($im, $i, $j, $dotcol);
045 }
046 }
047
048 imagestring($im, $font, $charwidth, $charheight/2, $pass, $fcol);
049
050 for($j=0; $j<$height*$fact; $j=$j+rand(3,6)) {
051 imageline($im2, 0, $j, $width*$fact, $j, $linecol);
052 }
053
054 for($i=0; $i<$width*$fact; $i=$i+rand(4,9)) {
055 imageline($im2, $i, 0, $i, $height*$fact, $linecol);
056 }
057
058 for($i=0; $i<$width*$fact; $i++) {
059 for($j=0; $j<$height*$fact; $j++) {
060 $x = abs(((cos($i/$cosrate)*5+sin($j/$sinrate*2)*2+$i)/$fact))%$width;
061 $y = abs(((sin($j/$sinrate)*5+cos($i/$cosrate*2)*2+$j)/$fact))%$height;
062 $col = imagecolorat($im, $x, $y);
063 if ($col!=$bcol) imagesetpixel($im2, $i, $j, $col);
064 }
065 }
066
067 for($j=0; $j<$height*$fact; $j=$j+rand(1,5)) {
068 for($i=0; $i<$width*$fact; $i=$i+rand(1,5)) {
069 imagesetpixel($im2, $i, $j, $dotcol2);
070 }
071 }
072
073 imagejpeg($im2);
074 imagedestroy($im);
075 imagedestroy($im2);
076 }
077
078 header("Content-Type: image/jpeg");
079 create_image();
080 ?>
You can try modifying line 7 and 8 for different background and foreground colour to see the effect. Line 9 are the words that are used randomly for generating the captcha (you can add as many as you wish). Then the html form (testcaptcha.html):
001 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
002 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
003 <html xmlns="http://www.w3.org/1999/xhtml">
004 <head>
005 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
006 <title>Captcha Test</title>
007 <style type="text/css">
008 <!--
009 body,td,th {
010 font-family: Arial, Helvetica, sans-serif;
011 }
012 -->
013 </style></head>
014 <body>
015 <form id="captcha_form" name="captcha_form" method="post" action="testcaptcha.php">
016 <img src="captcha.php" /><input name="userpass" type="text" />
017 <input name="submit" type="submit" value="Submit" /></td>
018 </form>
019 </body>
020 </html>
and finally the php program to process the form (testcaptcha.php):
001 <?php
002 session_start();
003 if ($_SESSION["pass"] == $_POST["userpass"]) {
004 echo "You have got the captcha right!";
005 } else {
006 echo "Sorry you failed the captcha";
007 }
008 ?>
This example should get you up and running immediately. Hope you can have fun playing with the example.