10.04
I consider myself a very security conscious person. In fact, I’d say that I’m just a tin-foil hat away from paranoia. Naturally, when I wanted a way for clients to contact Sneakybox without having to write an email I was a little wary of keeping my PHP code secure. After all, Spammers would love access to the PHP mail function on my web server.
In this tutorial I will show you how I stopped them getting their viagra pushing paws on my contact form as well and how to create one of your own.
How the script works
- User ticks what service they are interested in, then submits their name, email address, phone number and any extra notes via a HTML form.
- PHP script receives the submitted data
- PHP script strips off any invalid characters.
- Script makes sure the user’s IP address hasn’t posted a contact request in the past hour.
- If everything checks out, the script compiles the received data in to an email using the PHP mail function and sends it to a specified email address.
If the received data fails any of the checks the script will alert the user then exit.
Step One: The boring HTML bit
OK first we need to create our HTML form. I’m assuming you already have a basic grasp of HTML, otherwise you’re probably going to run in to some problems that aren’t covered in the tutorial…
<form action="contact_form.php" method="post">
The form tag contains our whole HTML form. We need to specify two parameters:
Method: How we’d like to send our user’s data – Our options are GET or POST.
GET submits the user’s data as part of the URL. There’s a long standing myth that GET requests are limited to 256 characters. It’s not true.
POST submits data after the HTTP header, out of the user’s view. There are several reasons why we’re using POST in this case, but my official reasoning is that W3C recommends using POST to submit user data. We want to stay within W3C’s guidelines as much as possible.
Action: Where to send the user’s data – We’re going to specify our PHP script’s location here. Let’s call it contact_form.php. Original eh?
It is possible to include the PHP script on the same page as the HTML but it’s good practice to keep them separate in that your HTML page is less cluttered it’s easier to make changes to either file.
Then it’s just a case of adding our form fields as below:
<input name="contact_web" type="checkbox" value="Web Design" />
<label>Web Design</label>
<input name="contact_dev" type="checkbox" value="Web Development" />
<label>Web Development</label>
<input name="contact_media" type="checkbox" value="Media Production" />
<label>Media Design</label>
<input name="contact_name" type="text" value="Your name..." />
<input name="contact_email" type="text" value="Your email address..." />
<input name="contact_phone" type="text" value="A contact number..." />
<textarea cols="3" rows="3" name="contact_desc">
If you have anything else to add, please do so here.
</textarea>
<input name="contact_submit" type="submit" value="Contact Me!" />
</form>
One thing to note about adding form fields is that our script will identify each field using the name attribute, so it’s important to give every one a unique name.
Step Two: The Database
In order to stop people sending more than one request an hour we need to keep a database of user IP addresses and timestamps which we can reference when a request is made. A simple MySQL database will do the job. Create a new database and paste the following in to your MySQL query window of choice:
CREATE TABLE `contact` (
`ip` char(15) NOT NULL,
`timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP
on update CURRENT_TIMESTAMP,
PRIMARY KEY (`ip`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Step Three: PHP
OK now we’re all set up, it’s time for the PHP script!
<?php
require_once 'htmlpurifier/library/HTMLPurifier.auto.php';
$purifier = new HTMLPurifier();
After starting the script with <?php we’re basically saying "Make the HTMLPurifier functions available for use, and call them using $purifier."
HTMLpurifier is an awesome tool for cleaning up user input data to eliminate XSS attack threats. I highly recommend you use it when cleansing data coming from a user. You can download it here. Upload it to your web server then alter the above “require_once” line to match your path.
mysql_connect("server", "username", "password")
or die("cannot connect to database." .mysql_error());
mysql_select_db("database")
or die("Cannot select Database:" . mysql_error());
Next we need to define our MySQL database connection details. The first line is where you put your server info (normally localhost but check with your hosting provider), database username and password. The second line is where you define what database to use. Use the database you created the contact table in.
$clean_name = addslashes($purifier->purify($_POST['contact_name']));
$clean_phone = addslashes($purifier->purify($_POST['contact_phone']));
$clean_email = addslashes($purifier->purify($_POST['contact_email']));
$clean_desc = addslashes($purifier->purify($_POST['contact_desc']));
$clean_check_web = addslashes($purifier->purify( $_POST['contact_web'])) ;
$clean_check_dev = addslashes($purifier->purify( $_POST['contact_dev']));
$clean_check_media = addslashes($purifier->purify( $_POST['contact_media']));
$clean_desc_wrapped = wordwrap($clean_desc, 100, "\n", true);
$ip= mysql_real_escape_string($purifier->purify($_SERVER['REMOTE_ADDR']));
Here’s our variables! We need somewhere to put the data that users send us, and we need to make sure it’s clean from dodgey characters when we do so. Each line is first adding an escape character with ‘addslashes’ to any characters which could cause us XSS problems, then HTMLpurifier works its magic.
wordwrap is used to wrap the ‘extra’ box text to a new line every 100 characters. ‘\n’ is a newline character. We also pull the user’s IP address in the bottom line.
$body =
"A new web form contact request has been receieved:
\n Name: ".$clean_name."
\n Phone Number: ".$clean_phone."
\n Email Address: ".$clean_email."
\n Interested registered in:
\n".$clean_check_web.",
".$clean_check_dev.",
".$clean_check_media."
\n".$clean_desc_wrapped;
Above is the body of the email that will be sent to us if the user is sucessful in their request. It’s a very simple mashup of all the variables used and a few new line characters.
if ($clean_name && $clean_phone && $clean_email) {
$check = mysql_query("
SELECT timestamp FROM contact WHERE ip = '".$ip."';
");
if (mysql_num_rows($check)){
$last_post = strtotime(mysql_result($check,0));
$time_now = time();
Here is where the important stuff starts happening:
- Obviously we dont want a user being able to send us a blank form, so we first say "If the variables clean_name, clean_phone, and clean_email exist (and are not empty in this case), move on…"
- Next we run an SQL query to see if there is a timestamp in our contact table that corresponds to the user’s IP address. We use mysql_num_rows to see if any results are returned. If a result is returned, move on…
If there was a result, it means the user has already sent a contact request in the past. Great! Now we need to know how recently it was.
MySQL and PHP store timestamps in completely different formats. MySQL will generally store a timestamp like "2009-10-04 06:38:10", whereas PHP uses the UNIX format, such as 1254701898 – that’s the number of seconds that have passed since Jan 1st 1970!
Luckily for us, PHP has a great function to convert the easily read MySQL timestamp in to UNIX time. That’s the strtotime() line, above. We’re basically saying, "convert the timestamp found in the first row of the SQL query $check to UNIX time and place it in the last_post variable."
We then need to know what the current UNIX time is, by calling the time() function and placing it in the time_now variable.
if(($time_now - $last_post) >= 3600 ) {
mail('Sales@sneakybox.com', "WEBSITE REQUEST FORM FROM $ip", $body);
mysql_query("
UPDATE contact SET timestamp = CURRENT_TIMESTAMP WHERE ip = '".$ip."';
");
echo "Thanks! A memeber of our team will be in touch as soon as possible!
<a href = 'http://www.sneakybox.com'>Back to Sneakybox</a>";
die;
}
else {
echo "You've already sent a request!
If you'd like to add any further information please email
<a href = 'mailto:info@sneakybox.com'>info@sneakybox.com</a><br />
<a href = 'http://www.sneakybox.com'>Back to Sneakybox</a>";
die;
}
Here’s our spam-stopper! We’ll take the time the user last made a request in seconds, and subtract it from the current time. If the remaining seconds are over 3600, or one hour, let the user send another request. If not, let them know that they’ve got to wait longer or send an email directly.
If the user has waited an hour since their last post, we need to:
- Send our contact request via the PHP mail function. Assuming your webhost has this set up, it will automatically send an email to the defined email address, using the defined subject, with the defined body – in that order.
- Update our contact table entry for the current user’s IP address with the current timestamp using an SQL query. ‘CURRENT_TIMESTAMP’ is a built in MySQL function. It does what it says on the tin.
- Let the user know they were sucessful in their request.
- Kill the script.
}
else {
mail('Sales@sneakybox.com', "WEBSITE REQUEST FORM FROM $ip", $body);
mysql_query("
INSERT INTO contact(timestamp,ip) VALUES(CURRENT_TIMESTAMP,'".$ip."');
");
echo "
Thanks! A memeber of our team will be in touch as soon as possible!
<a href = 'http://www.sneakybox.com'>Back to Sneakybox</a>";
die;
}
The first ‘}’ is to close the IF statement that checks whether the current user has posted a contact request before. If they haven’t:
- We can safely send the user’s request in the same way as above.
- We also need to add this information to our contact table. However, as the IP address hasn’t been in the table before we need to run an SQL ‘insert’ query rather than an ‘update’. We’re inserting the user’s IP address in to the table along with the current time.
- Let the user know they were sucessful.
- Kill the script.
}
echo "
Sorry, All fields in the form are required
Our Javascript validator should have picked this up before
submitting but it looks like you have javascript disabled.
<a href = 'http://www.sneakybox.com'>Back to Sneakybox</a>";
die;
?>
The ‘}’ is to close off the first IF statement. The one that makes sure the submitted user fields aren’t blank, remember?
If the user didn’t meet these conditions, let them know then die. Done!
You can download the full PHP script here.
If you have any questions etc please let me know in the comments.
Aaron

This was really clear. nicely done
Thanks a lot Mike!
Aaron,
Can I ask you a favor?
Sure what’s up?
Good one.. Thanks
very good.
As Mr. Burns would say, “E-X-C-E-L-L-E-N-T!”
Thanks for the wonderful effort
http://www.rm-4.com/vb
http://www.dreamksa.com/vb
Hi, Aaron — One more comment…I especially like how you “over-commented,” and explained every nuance. That is very helpful to a neophyte like me.
How about something on querying a MySQL table, then displaying the results in an HTML table, based on the fields that the user selects. Nothing except PHP, MySQL, and HTML No OOP.
Thanks again.
good i want more
Is there anything in particular you would to see?
nice lesson
movies4world.com
hanks a lot, Mike!
http://aldawaghranet.com
this is very good, thanks alot
thanks alot
its very helping………
[...] 2)How to create a secure PHP contact form Learn how to create a spammer-stopping PHP contact form for your website. [...]
Another great post.
Thank you for the information, Its good to see such quality posts.
Im subscribing to your blog.
Keep them comming.
Classified Posting Software
Another great post.
Thank you for the information, Its good to see such quality posts.
Im subscribing to your blog.
Keep them comming.
Software for craigslist
[...] person. In fact, I’d say that I’m just a tin-foil hat away from paranoiaOriginal post: Click Here…Read Other Interesting Posts in PHP TutorialsThe ways to create thumbnail image by php with [...]
this was very clear and good
thank you very much mike
keep it up
Hi there
First of all, thank you so much for posting this. It is one of the most comprehensive and easy to understand tutorials I have come across… ha ha even then I can’t get it to work!
I am in the process of setthig this up and suspect I have done something wrong along the way. The problem is when I click ‘contact me’ to a blank page( http://bayhomeopathy.slicetest.co.nz/contact_form.php).
I don’t really know even where to start looking or what you might need that could help in problem solving this. I have copied your exact code, I know I have the correct database details. I see that HTMLPurifier is best with php 5.1.2 and higher. The database I am using only has 5.0.27. I would be grateful for any tips on where I might be going wrong.
The site I am working on at the moment is only tiny but I was hoping to use on a larger site once I ‘practiced’ on the smaller site.
Hi Lisa,
Sorry for the delay in replying – Your comment was lost in mountains of spam! :/
Did you ever get the contact form working?
Cheers,
Aaron