WP-Mix

A fresh mix of code snippets and tutorials

PHP securing email scripts

This tutorial describes several important strategies for securing contact forms and scripts that make use of PHP’s mail() functionality. If your script is sending email based on user input, these tips will help to keep things safe and secure.

Don’t reinvent the wheel

Before getting too deep into writing your own contact form script, it may save you some time to grab one of the many scripts that are available around the Web. Then you can simply check that the recommended security tips in this article have been applied to whichever script you feel like using.

Even easier, if you are using WordPress, you can save time and grab a copy of my very secure contact-form plugin, Simple Basic Contact Form, available for 100% FREE at the WP Plugin Directory. Many of the security techniques described in this article are implemented in SBCF to help make it super secure.

That said, let’s check out some essential security tips for email scripts.

Check for bad emails

There a many ways to check that the user’s email address is properly formatted and free from any malicious strings. Doing so helps to ensure that your email script doesn’t end up getting abused by spammers to send out their crappy spams all over the place. Here are two ways of going about checking for bad email input:

1) Use PHP’s filter_var() set to FILTER_VALIDATE_EMAIL:

filter_var($email, FILTER_VALIDATE_EMAIL)

2) Or hand-roll your own custom filter function, for example:

function shapeSpace_check_email($email) {
	return preg_match('#^[a-z0-9.!\#$%&\'*+-/=?^_`{|}~]+@([0-9.]+|([^\s]+\.+[a-z]{2,6}))$#si', $email);
}

This simple function checks the email input variable and returns true or false, depending on whether or not any malicious characters are found in the email string.

Regardless of how you do it, checking for legit email input is critical to security.

Check bad strings

In addition to checking the user’s specified email address, it’s also important to check other input data, such as the email subject and message. Here is an example function that checks for any bad strings:

function shapeSpace_check_string($string) {
	$bad_strings = array(
		'content-type:', 
		'mime-version:', 
		'multipart/mixed', 
		'Content-Transfer-Encoding:', 
		'bcc:', 
		'cc:', 
		'to:',
		'from:',
	);
	foreach($bad_strings as $bad_string) {
		if (eregi($bad_string, strtolower($string))) {
			return false;
		}
	}
	return true;
}

So you can call this function for each input; it will return true or false depending on whether or not any bad strings are found.

Check for newlines

Newlines included in posted email data (whether intentional or not) are considered malicious and should be removed, or the email should be rejected. Here is an example of a function that checks for any newline shenanigans:

function shapeSpace_check_newlines($string) {
	if (preg_match("/(%0A|%0D|\\n+|\\r+)/i", $string) != 0) {
		return false;
	}
	return true;
}

If any newline characters are found, the function returns false, otherwise returns true.

Check POST request

Another good security tip is to deny any requests that aren’t sent via POST request. Something like this should do the trick:

function shapeSpace_check_post_request() {
	if ($_SERVER['REQUEST_METHOD'] != 'POST'){
		return false;
	}
	return true;
}

That will make sure that other types of requests such as GET, HEAD, PUT, et al are not accepted.

Hidden/reverse captcha field

Another way to secure your contact forms is to add a hidden field that only bots can see. So when a human submits the form, the hidden captcha field will be empty. But if a bot submits the form, most likely they will complete all fields, including the hidden captcha. That enables you to check the hidden field and reject the POST request if the hidden field is not empty.

To do so, first add a hidden field to the form, something like this:

<input type="text" name="gotcha" class="gotcha" value="">

Then make sure the field is hidden via CSS:

.gotcha { display: none; }

Lastly, in your contact-form processing script, check the value of $_POST['gotcha'] to make sure it is empty. Here is a basic example:

<?php if (!empty($_POST['gotcha'])) die(); ?>

Of course, you will want to customize and integrate according to your own needs/goals.

Use cookies

If you don’t mind requiring cookies, you can begin a session for the form and then check if $_COOKIE['PHPSESSID'] is set. If not, let the visitor know that cookies are required to use the form. Bots generally don’t waste time with cookies, so this technique should help to keep bots and their scummy spams at bay.

Random string captcha

Another technique is to generate a random string when the visitor visits the form. You can save the string as a session variable, and break into several pieces, with each piece displayed in a different color. Then you can ask the user to enter the green characters before clicking the submit button. When the form is submitted, you can check whether or not the user entered the correct information.

This technique will defeat most bots, and is pretty easy for the user (compared to other hard-to-read captcha images). Alternately instead of a random string, you can ask a simple math question. If you know your audience will speak the same language, another strategy is to ask a simple question. Or whatever, you get the idea.

Check for duplicate data

Spambots typically repeat the same data to populate multiple fields. So a good way to check for bots is to loop through the POST variable and check for duplicate data. Here is an example:

<?php $i = 0;
foreach ($_POST as $key => $val){
	if (stristr($val, 'http:')) $i++;
	if (stristr($val, 'https:')) $i++;
	if (stristr($val, '[url=')) $i++;
	if (stristr($val, '[url]')) $i++;
}
if ($i > 1) die(); ?>

Here we are looping through and checking for http:, https:, [url=, and [url], which spambots are known to use when submitting their stupid spams. Feel free to keep a log and update the strings with any new values that you encounter.

Bonus security tips

Here are a few more security techniques to help validate contact forms and keep spam away from your inbox.

Strip HTML entities:

// Strip HTML entities
function strip_html_entities($str) {
	return preg_replace("/&[a-z0-9]{2,6}+;/i", '', $str);
}
if (stristr($str, '&'))) $str = strip_html_entities($str);

Check zip codes:

// Check zip codes
if ($_POST['zipcode']) {
	$check = strlen(trim(preg_replace("/[^[:digit:]]/", '', $_POST['zipcode'])));
	if ($check < 5) die();
}

Protect against header-injection attacks:

// Protect against header injection attacks
function check_header_injection($fields) {
	$injection = false;
	for ($n = 0; $n < count($fields); $n++) {
		if (eregi("%0A", $fields[$n]) || eregi("%0D", $fields[$n]) || eregi("\r", $fields[$n]) || eregi("\n", $fields[$n])) {
			$injection = true;
		}
	}
	return $injection;
}
	
$from    = $_POST['from'];
$email   = $_POST['email'];
$subject = $_POST['subject'];

$result = check_header_injection(array($from, $email, $subject));

if ($result == true) die();

You can also extend/apply these techniques to validate phone numbers, email addresses, et al. Another useful validation technique is to check empty values for required fields such as name, address, email, message, subject, etc. In general required fields should never be empty.

Going further

These tips explain some important security tips for any contact form or email script, but of course much more is possible. If you’re thinking about rolling your own contact-form script, I recommend searching around for additional techniques and tips to lock things down as much as possible. These days, you just can’t risk getting your domain/email flagged as “spam” by Google et al, just because some sloppy coding allowed some lowlife scumbag to take advantage of an insecure script.

Check out these WP-Mix posts for more email security techniques:

★ Pro Tip:

USP ProSAC Pro