PHP Application Security – Securing the mail() function

Sending email through PHP is quite common in many web applications. I frequently do this when implementing a contact form. Utilizing my skills in HTML, CSS, AJAX, and PHP I create a nice contact form that looks good, functions well, and pleases the user. However, much to my shame, I discovered that my implementation was often quite insecure. Below you will find my shameful code, as well as tips on patching this type of security hole.

THE PROBLEM

$to = $_POST['to_email'];
$subject = $_POST['subject'];
$message = $_POST['message'];
$headers = "From: ".$_POST['from_email'];
mail($to,$subject,$message,$headers);

This is pretty much what I had my contact form posting to. All I focused on was two things: all fields needed to be dynamic and the script needed to send mail somewhere. It got the job done, but there is absolutely no security measures in place. Anyone could set up an automated script to post to this page. Email header injection can allow the evil attacker free access to modify the email, change who it is sent to, send it to multiple people , change the content type and more. A line feed must be added for every header. Someone could then post the following:

victim1@email.com%0ABcc:victim2@email.com

Using this and similar methods, the user has full control over our email script.

THE SOLUTION

First of all, you’ll notice that I’m not checking any data that is coming into my script. This security measure should always be in place. Therefore, each incoming piece of data should be sanitized and validated. Also, it’s good to make sure we have the latest release of PHP installed on our server. Newer versions of PHP will have improved security measures for email header injections.

Another added security measure that we can add is a token. A token is a random string generated and passed through the form, and stored in a session or cookie. Our mailing script can then verify that the users saved token matches what was passed to it. This way, we make sure that the mail is being submitted through our form, and not some external script. With these security measures in mind, let’s strengthen the security of our mailing script.


/********* form.php *********/

// create a token

session_start();

$token = md5(uniqid(rand(), TRUE));

$_SESSION[‘token’] = $token;
// insert token into a hidden input inside the form
input name="token" type="hidden" value=" $token; "
/********* form_submit_here.php *********/

session_start();

function check_token($token){

return($token == $_SESSION[‘token’]) ? true : false;

}

function check_string($string_to_test){

$bad_words = array(“cc”, ”to”, ”bcc”, ”Content-Transfer-Encoding”, ”content-type”, ”mime-version”, ”multipart/mixed”, ”%0A”, ”%0D”, ”\r”, ”\n”);

foreach($bad_words as $bad_word){

if(stripos($string_to_test, $bad_word) !== false){

exit; // injection attack detected

}

}

return $string_to_test; // no injection attack detected, string is safe to use.

}

if(!check_token($_POST[‘token’])){

exit; // token doesn’t match, post came from outside our form.

}

$to = check_string($_POST[‘to_email’]);

$subject = check_string($_POST[‘subject’]);

$message = check_string($_POST[‘message’]);

$headers = “From: “.check_string($_POST[‘from_email’]);

// token is valid and all our dynamic variables check out ok. Send the email.

mail($to,$subject,$message,$headers);

Of course, there is much here that you may wish to alter such as better error handling, error logging, etc. By spending just a few extra minutes of typing, we have greatly strengthened the scripts security measures. Whatever variation of this script that you may use, make sure you validate input when working with dynamic variables. 

Jacob Haskins

Jacob Haskins

Jacob has always had an interest in learning and problem solving. Whether working with Objective C, PHP, Actionscript, FLEX, JavaScript or any other language, he finds that there is always something new to learn. He enjoys development projects most when getting to use new technologies.

Leave a Reply

Your email address will not be published. Required fields are marked *

Categories

Search

Recent Posts

Most Common Tags