Build Your Own Captcha and Contact Form

Build Your Own Captcha and Contact Form

Ever get hit with spam through the contact form on your personal site? Well, here is a short tutorial on how to build a custom captcha to keep the bad guys out.

Step 1: captcha.php and the Session

First we need to build a new PHP page and save it as captcha.php. Then, in out new script, open a server session by using the session_start() command. Also, code an empty variable named “string”. We will use this variable later to hold our randomly generated captcha text.

<?php

session_start();

$string = '';

?>

Step 2: Random String

Next, we need to write a for loop that will generate a random string. We will then take this random string and set it in a session variable called “random_code“.

<?php

session_start();

$string = '';

for ($i = 0; $i < 5; $i++) {
	$string .= chr(rand(97, 122));
}

$_SESSION['random_code'] = $string;

This for loop, you'll notice, adds a lower case ascii character, using the chr() function, to our $string variable on every pass. My example generates 5 characters, but you can adjust that number by changing "$i < 5" in the loop statement to something more custom, like "$i < 7". Once your loop is complete, make sure you define your session variable.

Step 3: Storage Folder and Colors

This is where the tutorial gets a little more complicated. Next we need to define a storage folder for the font we are going to use, build the base captcha image, and define the colors we'll use to fill our image. This is all simple code, but they're functions that don't get used often by developers.

$dir = 'fonts/';

$image = imagecreatetruecolor(170, 60);
$black = imagecolorallocate($image, 0, 0, 0);
$color = imagecolorallocate($image, 200, 100, 90); // red
$white = imagecolorallocate($image, 255, 255, 255);

Initially, I'm just defining the folder where my fonts are stored in the $dir variable. The $image variable, where we use the imagecreatetruecolor() function is the money spot. This is where the base captcha image is built using PHP. The function imagecreatetruecolor() returns an image identifier representing a black image of the specified size. As you can see, I'm making my image 170px wide by 60px tall.

Finally, in this step, I define some colors we can use in our final image. The numbers passed to the imagecolorallocate() function are RGB values.

Step 4: Building the Image

Next, we're going to fill our image with a white rectangle, which will act as the image background, and then add our random text string to the image.

imagefilledrectangle($image,0,0,200,100,$white);
imagettftext($image, 30, 0, 10, 40, $alt, $dir."arial.ttf", $_SESSION['rand_code']);

The imagefilledrectangle() function draws a rectangle in the specified image. The four numbers passed in the function represent coordinates for the corners of the rectangle. Make sure the rectangle you draw here is larger than the base image. You'll notice my rectangle is 200px wide and 100px tall.

The imagettftext() lets us add text to an image using True Type fonts. And you'll see that this function that can handle quite a few parameters. I'd like to highlight all of the different parameters in this function, but you'll only need to remember a few.

imagettftext($image, $font_size, $angle, $x, $y, $color ,$font_file ,$text);

Once you compare my example to the code immediately above, you'll see that the values passed to the imagettftext() function are easy to understand. First is the image, then font size, angle of the text, the x and y coordinates of the text (starting with the top left corner), text color, the location of the font file, and finally the text (our random string).

Step 5: Image Final

Next, with our script, we need to tell our browser what type of image we are using, with a header() function, and build the final image. These functions are so straight-forward, not much can be explained about them. Also, don't forget to close your PHP script.

header("Content-type: image/png");
imagepng($image);

?>

Once previewed in a browser, you script should generate a png image that contains some text. If you receive errors, make sure your script can link to your .ttf font file, and that you have created the empty $string variable from earlier in the tutorial.

This is what the final code for your captcha.php page should look like:

<?php
session_start();

$string = '';

for ($i = 0; $i < 5; $i++) {
	// this numbers refer to numbers of the ascii table (lower case)
	$string .= chr(rand(97, 122));
}

$_SESSION['rand_code'] = $string;

$dir = 'fonts/';

$image = imagecreatetruecolor(170, 60);
$black = imagecolorallocate($image, 0, 0, 0);
$color = imagecolorallocate($image, 200, 100, 90); // red
$white = imagecolorallocate($image, 255, 255, 255);

imagefilledrectangle($image,0,0,399,99,$white);
imagettftext ($image, 30, 0, 10, 40, $color, $dir."arial.ttf", $_SESSION['random_code']);

header("Content-type: image/png");
imagepng($image);
?>

Step 6: contact.php

Build a new PHP contact page and save it as contact.php. This page will contain our contact form that will validate using our captcha.

Step 7: HTML & CSS

Let's add an HTML form to our contact.php page. Pay particular attention to the image source we use for the random text.

<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post" enctype="multipart/form-data">
	<p><input type="text" name="name" /></p>
	<p><input type="text" name="email" /></p>
	<p><textarea name="message"></textarea></p>
	<img src="captcha.php"/>
	<p><input type="text" name="code" /></p>
	<p><input type="reset" name="submit" value="Send" /></p>
</form>

You can also add the following bit of CSS to your page to make it look better than default.

<style type="text/css">
form {
	margin:0;
	padding:0;
}
input {
	padding:2px;
	width:200px;
}
textarea {
	padding:2px;
	width:200px;
	height:100px;
}
.button {
	width:60px;
}
p {
	margin:0 0 5px 0;
	padding:0;
}
.error {
	color:#FF0000;
	margin:0 0 10px 0;
}
.accept {
	color:#339966;
	margin:0 0 10px 0;
}
</style>

Step 8: Validate with PHP

Now that our form is built and we have out captcha image displaying, all we need to do now is validate our form, being sure to include some validation rules for out captcha.

Below is what the final validation PHP should look like. Two important features required for this validation process are session_start(); and the if($_POST['code'] == $_SESSION['rand_code']) elements. These allow us to access out session variable and check it against the text someone writes in the code field of our form. Without these, our captcha would be useless.

<?php
session_start();

if(isset($_POST['submit'])) {

	if(!empty($_POST['name']) && !empty($_POST['email']) && !empty($_POST['message']) && !empty($_POST['code'])) {

		if($_POST['code'] == $_SESSION['rand_code']) {

			// send email
			$accept = "Thank you for contacting me.";

		} else {

			$error = "Please verify that you typed in the correct code.";

		}

	} else {

		$error = "Please fill out the entire form.";

	}

}
?>

If you know much about PHP, the rest of this validation should be easy to understand. We are essentially looking to make sure none of our form fields are empty. If they are empty, errors are thrown to make sure our user inputs information. You'll also notice I am not validating whether the email is well formed, which is something your should should include. Email validation is done using regular expressions.

Here is what your final contact.php file should look like. I am including the CSS, which you may want to drop in its own CSS file.

<?php

session_start();

if(isset($_POST['submit'])) {

	if(!empty($_POST['name']) && !empty($_POST['email']) && !empty($_POST['message']) && !empty($_POST['code'])) {

		if($_POST['code'] == $_SESSION['rand_code']) {

			// send email
			$accept = "Thank you for contacting me.";

		} else {

			$error = "Please verify that you typed in the correct code.";

		}

	} else {

		$error = "Please fill out the entire form.";

	}

}

?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Contact Us</title>
<style type="text/css">
form {
	margin:0;
	padding:0;
}
input {
	padding:2px;
	width:200px;
}
textarea {
	padding:2px;
	width:200px;
	height:100px;
}
.button {
	width:60px;
}
p {
	margin:0 0 5px 0;
	padding:0;
}
.error {
	color:#FF0000;
	margin:0 0 10px 0;
}
.accept {
	color:#339966;
	margin:0 0 10px 0;
}
</style>
</head>

<body>

<?php if(!empty($error)) echo '<div class="error">'.$error.'</div>'; ?>
<?php if(!empty($accept)) echo '<div class="accept">'.$accept.'</div>'; ?>

<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post" enctype="multipart/form-data">
	<p><input type="text" name="name" /> Name</p>
	<p><input type="text" name="email" /> Email</p>
	<p><textarea name="message"></textarea></p>
	<img src="captcha.php"/>
	<p><input type="text" name="code" /> Are you human?</p>
   <p><input type="submit" name="submit" value="Send" class="button" /></p>
</form>

</body>
</html>

You're Done

Once your validation is working, your captcha contact form should be working great. What are your thoughts? Thanks so much for reading.


Add Comment

Discussion 144 Comments

Comment Page 2 of 2 1 2
  1. Author

    First, thank you to everyone for the replies. I’m glad to see this sparked some discussion.

    And I’m sorry for the two typos in the code. They have been fixed in the source download.

  2. As always we find here resources than make us back one and once again;
    Thanks nettuts people!

  3. Fynn says:

    A lot of complaints about the Captcha script I see..

    But what is a more secure way than? Using something like: “Give the answer of 5 times 5″ ? Something like that?

    I like the tutorial though, I’ve never created images with php.

  4. Mark says:

    Personally, I would include a hidden field in the form and check to see if it has been filled.

    I would also have an alternate check where the person might have to type in the colour of a particular letter instead of the random string itself.

    • Matt Hopkins says:

      I was receiving a minimum of 1 spam comments every 2 minutes until I implemented a version of the hidden field technique with an extra checksum and it worked like a charm. It should at least keep the basic comment posting bots out.

      I think the idea of captcha is really annoying from a user perspective but I appreciate the code on this page, it really helped me out with modifying my site.

  5. DemoGeek says:

    Even though it’s a good start I wouldn’t rhyme with this implementation. As many of you pointed out here this can easily be botted and that of course deceits the purpose. But certainly you can use this as a base and improve on it 200 times to have a secure CAPTCHA implementation.

    • someturkishguy says:

      come on! who will try to write a bot script to recognise this typings? i bet no one will try that.. this just simply makes users avoid registering or posting in a spamming manner…

  6. andrea says:

    great tutorial for a beginner like me. i will also have to try some of the suggestions other people suggested for this too.

  7. Steinip says:

    Even though a lot of people seam to nitpick on how easy it is to crack, PEOPLE must not forget about that he spent time on creating this code and tutorial for basic users to actually comprehend what it’s about, the basic idea and for that people should be thanking him :) just like im about to do.

    Good tutorial and i just love your website, it is so clean and the layout makes you fall in love with it.

  8. Matt Damon says:

    I’m new @ this:

    ok, so I put this line in the contact.php file and it sends an e-mail but it’s empty and it doesn’t display the e-mail it’s sending it from:

    // Send
    mail(‘xxxxxxxxxxx@gmail.com’, ‘email’, ‘message’);

    what am I doing wrong? or what am I missing?

    • Zac Vineyard says:
      Author

      Hi Matt,

      Try something like:

      // Send
      mail(‘caffeinated@example.com’, ‘My Subject’, $_POST['message']);

      Also, make sure your host provider / server supports the mail function.

  9. Tomas says:

    Nice tutorial, As I read this I decided to post my cusom Captcha Source code.

    If you interested, take look at:

    http://www.dobrotka.sk/blog/creating-a-captcha-with-php-and-mysql-database/

    Maybe i`s not the best one, but you can try and customize if you like.

  10. Lev says:

    Hi
    I love the discusion and I so new to this that my ming is going nuths.
    I love the thutorial i ma use to cgi forms and php is very very new to me,I agree with alot of you, script mightbe simplistic or mightbe not scure as it should ,but remeber this is not a final product and it is given as somthing to fall back on and somthing tostart form…
    Now I need some help
    I used to cgi forms and on this tutorial
    form is not pointing to mailing engine, unless I miss somthing..

    <form action=”" method=”post” enctype=”multipart/form-data”>
    I ma use to point from action to a mailing engine, so how do i make this form mailed?

    I try another miler form the i loos COPTCHA function..
    Any way any sugestion would be appreciated
    ( soory for my typing not the best , i might have some typos)

  11. Henrik says:

    http://www.sitepoint.com/blogs/2009/05/14/captcha-alternatives/

    Tsk tsk, avoid CAPTCHAs they are bad for the user interface, often you can avoid them.

  12. Nick says:

    The line exit(); should also be included in captcha.php. I’m a beginner at this and it took me a while to figure this out! Before I added that line, captcha.php was creating a blank PNG image without any letters on it :(

    Good tutorial though, thanks for posting this, great for a beginner like me.

  13. Ely says:

    Great tut! The Captcha image concept is very similar to using a randomly generated token, storing in it, and then checking it upon arrival at our processing script. Very good tut!

  14. simo says:

    great tut…thank u

  15. Alexey says:

    This tutorial has several critical mistakes, which make it unworkable.

    1) on the 4th step: imagettftext($image, 30, 0, 10, 40, $alt, $dir.”arial.ttf”, $_SESSION['rand_code']); – $alt doesn’t exist, there must be $color;

    2) on step 7: <form action=”" method=”post” enctype=”multipart/form-data”> – action of the form must be the link to the file where the validation is taking place.
    enctype=”multipart/form-data” isn’t needed here.

    3) on step 7: – type must be “submit”.

  16. Liz says:

    I’m sorry but his is the worst tutorial I’ve ever seen on here, you have left out variables in one part and added them later etc need i go on, not too mention the type=reset name=submit situation in step 7 then changing it at the end.
    Awful Awful Awful!!!!

    • Dan says:

      Also the $alt in one part is changed to $color in another, also, all the undefined variables at the end such as $error and $accept makes no sense at all.
      I’m surprised this tut was not moderated in the same way this comment will be.

      That said, taking the time to whip a tut for a few extra bucks is I suppose a worthy enough cause.

  17. Zoran says:

    Thank you for this… I just can’t understand why some people here are complaining about things like it can be hacked, it is XSS vulnerable.. bla bla bla.. This is a tutorial about basic captcha and it is quite helpful.. Like many said before me, if someone complains he/she should write a better one and stop complaining, you make me sick

  18. ian says:

    Can you guys at least make sure your tutorials work?
    Can you check the comments to see the mistakes made and update tutorials?

    Two errors in the imagettftext() $alt should be $color and $rand_code should be $random_code.

    Also you should not be sending the header() after you create the header with the session_start()

    Both these simple mistakes make the code unworkable and for someone learning PHP these might be really big problems for them.

    And this tutorial is over a month old?

    The author of this obviously did not even test their code cause it would fail right?

  19. Andy says:

    Nice Read and really a Good article.

  20. san says:

    Nice tutorial dude. can i put a back ground image on that generated capcha ?

  21. Chris the Developer says:

    Another change to note is that:

    $_SESSION['rand_code'] = $string;
    imagettftext ($image, 30, 0, 10, 40, $color, $dir.”arial.ttf”, $_SESSION['random_code']);

    the session keys should probably match…

  22. เพชร says:

    I prefer recaptcha anyway.

  23. Hugo says:

    Where would I put

    // Send
    mail(’mymail@example.com’, ‘My Subject’, $_POST['message']);
    In this document?

  24. Alex88 says:

    Let us treat the inner biomedical issues, not just the outer behavioral issues, and maybe these kids will simply act better. ,

  25. designfollow says:

    thanks for this great tutorial.

  26. David Moreen says:

    Even though this captcha does not seem to secure, I still believe that this is a wonderful starting point.

  27. Empiric Tech says:

    Nice Good Job

  28. Empiric Tech says:

    Nice
    Its Good Because Define With Easy Way

  29. paganproger says:

    Thanks fo this tutorial. Very useful to me.

  30. CSSReX says:

    Thanks for this tutorial. I am using another captcha script with your this form :)

  31. Christophor S. Wilson says:

    Thanks so much for posting this tut.

    One question is there a way to refresh the image?

  32. ERiK says:

    got it showing up ..the captcha but how do i send it to an email address? how can i add lines or blur the captcha? thanks for any help. Great script.

  33. CL says:

    nice one, but I would recommend hashing the session key for validation

  34. Constantin says:

    i already sent it to php.net too..

    this is respons for
    adam at worldwrestlingmania dot cjb dot net
    06-Dec-2009 04:35(here http://php.net/manual/en/function.imagettftext.php)

    and for all that’s using captcha to prevent send information in a form using a robot.

    People you don’t need captcha!!!! There is another convenient method , to protect a website for spamming and is much simple:

    Let’s consider the 1st page(with the form) and let’s say the second … index.php and receiver.php

    index.php:
    ———————————————————————-

    index.php

     
    A form without captcha!!!

    ———————————————————————-

    receiver.php
    ———————————————————————-
    <?php
    //receiver.php
    function protectform(){
    if($_SERVER["REQUEST_METHOD"]!='GET'){

    $servername=$_SERVER["SERVER_NAME"];
    $noterror=true;
    if (isset($_SERVER["HTTP_REFERER"]))
    $gethost=Parse_url($_SERVER["HTTP_REFERER"]);
    else
    $noterror=false;
    $pimp=false;
    if (!$noterror )
    $pimp=true;
    if(isset($gethost))
    if ($gethost['host']!==$servername)
    $pimp=true;

    if ($pimp){
    //print_r($gethost);
    die('Go away hacker!');
    }

    }
    }
    protectform();
    if(isset($_REQUEST['send'])and (trim($_REQUEST['data'])!='') ) echo('We already send to this page this value: '.$_REQUEST['data'].'’); else echo(‘Please try to fill something in that form!’);
    ?>Return to my form
    ———————————————————————-

    how to probe it?
    well let’s say you already upload it on
    http://www.example.com/myfolder/ index.php and receiver.php

    so try to digit
    http://www.example.com/myfolder/index.php

    now fill the form’s value…and click send.
    now is redirected to receiver.php and you see the right value.

    Let’s probe the vulnerability of the script:
    digit again
    http://www.example.com/myfolder/index.php
    now when you see the form press File/Save as from the browser’s menu and save it on desktop like index.html

    now try to open with notepad to edit it and change this line:

    to something like this:

    now save it and double click it from desktop.
    well what you see when you already fill that text form and you send the data to http://www.example.com/myfolder/ ?

    (For php beginners:www.example.com can be http://www.banana.com too , i don’t know where you will probe this software)

    to all people who said :”php is unsecure” i respond with :
    I am writing scripts from 1992, my opinion like expert is : php rooooooooolez!!! people if you don’t know how to write scripts in php try php.net to learn something. me,Constantin

  35. Robin says:

    It would have been wonderfull if it could work…
    tried all the options this is tutorial but nothing ..
    feels like now wasted my 1 hour just to do this
    :(

    Thanks but no Thanks

  36. Ari says:

    Just tested, it’s works fine. Only problem is when i included it to my page, submit button redirects back to index.php and doesn’t work. Anyone how i can fix this?, i got empty head now… :D

  37. Mark says:

    been looking for a captcha for a new project, bookmarked!

  38. Brixter says:

    I also had created captcha for my articles and I am looking for a way to distort the text or add some random colors on each text and add some background images.

  39. Wiyono says:

    Don’t work…

  40. Wiyono says:

    I have error:

    Warning: imagettftext() [function.imagettftext]: Could not find/open font in /var/www/vhosts/misha-tattoo.de/httpdocs/content/php/captcha.php on line 23

    Warning: Cannot modify header information – headers already sent by (output started at /var/www/vhosts/misha-tattoo.de/httpdocs/content/php/captcha.php:23) in /var/www/vhosts/misha-tattoo.de/httpdocs/content/php/captcha.php on line 25
    ‰PNG  IHDRª<y+¶‹¤IDATxœíÑA À0À¿ç!c& z×;3‡ª·À&ûÓìO³?Íþ4ûÓìO³?Íþ4ûÓìO³?Íþ4ûÓìO³?Íþ4ûÓìO³?Íþ4ûÓìO³?Íþ4ûÓìO³?Íþ4ûÓìO³?Íþ4ûÓìO³?Íþ4ûÓìO³?Íþ4ûÓìO³?Íþ4ûÓìO³?Íþ4ûÓìO³?Íþ4ûÓìO³?Íþ4ûÓìOû uj×O4IEND®B`‚

    Thank you

  41. Matthew says:

    Heya,

    I got this working once I found a ttf font to use since I don’t have Arial ttf, but OpenType. Great tutorial for a simple, captcha. I’ll use it as a base for dev on some heavier ones.

    Kewl.

  42. Joeyj01 says:

    Nice tut. Great for coders but I wonder did you ever use a form builder? http://bit.ly/lJrEzs

  43. NAN says:

    I think that if your going to write a tutorial, you first need to make sure you code is correct before it is published.. Its even worse that the site it is on didnt check their writers code prior to putting it on their site.

    Secondly, if your going to write a tutorial about a contact form…why do you leave it unfinished and not include the code for showing people how to get the email to send to the creators email address.

    Its a shame.

  44. soegengku says:

    good job! nice tutorial … :-)

  45. webgoonie says:

    I was so excited to find this tutorial until I found that it doesn’t work even from the source code to the examples off the website, can somehow we revisit this tutorial?

  46. Stephan says:

    Still doesnt work for me :S

  47. Zeeshan Parvez says:

    How come your background doesn’t have cool designs?

Comment Page 2 of 2 1 2

Add a Comment

To add a code snippet to your comment, please wrap your code like so: <pre name="code" class="html">YOUR CODE</pre>. You can replace the class name with "js," "css," "sql," or "php." If there are any "<" or ">" within your code, please search and replace them with: &lt; and &gt; respectively.