PHP: Secure Form Mailing

CGI forms have been notorious for abuse by spammers. The most used one of all is the famous formmail.cgi script. Most people have either moved on to other more secure scripts or turned to PHP for emailing their forms. That's all well and good however PHP can be abused just as much. I started to receive a lot of spam through the script on my business site a few weeks ago so I put in a simple trap in the script which checks the content entered before emailing it to me and returning the visitor to a thank you page. If the trap is triggered the visitor gets set to Google.com instead (bye bye!). Nothing's been received since. I'll go into the trap more in my PHP learning section when I start to explore string functions.

However the other day I received an attempt to use my web form to spam others. Below is a copy of the email I received, with the intended recipient's email blanked out. I've added an X in the middle of my domain to prevent other spambots picking it up, and XXX to the IP recorded.

From: sift4526@3emediaX.co.uk
Company: sift4526@3emediaX.co.uk
Telephone: sift4526@3emediaX.co.uk
Mobile: moonlight
Content-Type: multipart/mixed; boundary=b3b8beb3795e1c824f717fcad62d230f
MIME-Version: 1.0
Subject: the
bcc: cxxxxxxxx9@aol.com

This is a multi-part message in MIME format.

–b3b8beb3795e1c824f717fcad62d230f
Content-Type: text/plain; charset=\"us-ascii\"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit

pa apers ivry mornin . ayciption at th hite ouse. mong th casulties was so
–b3b8beb3795e1c824f717fcad62d230f–

.

Fax: sift4526@3emediaX.co.uk
Location: sift4526@3emediaX.co.uk
Country: sift4526@3emediaX.co.uk
E-mail: sift4526@3emediaX.co.uk
Query: sift4526@3emediaX.co.uk

IP: XXX.174.190.170

So that's what I received. Why didn't it work? Well I control the headers of my script and set it to come from my web site and not the address entered in the from box. I originally did this because I don't believe that everyone has an email address (my Dad doesn't for a start!) and also just because I decided I wanted to control the headers more to be recognisable in my spam programs. However after a check with a very knowledgeable friend I've also learnt my script is more than secure (yay!). It sounds bizarre maybe that I'm saying this but until the above email I've never had or seen an attempt on what is commonly known as a header injection. Since the spamming happened I started to grab the IPs of the users too so the attempt has been emailed to the abuse address of the IP owner and the IP has now been blocked from my web site.

Last week I had a client email me in a panic. A form mailer that his previous developer had set up had been disabled by his host as it was being used for spamming and sure enough there was the typical, most used method of sending email using PHP:


mail("client@hisdomain.com", "Web Form Results", $msgbody, "From: $name <$email>");

I changed this to


mail("client@hisdomain.com", "Web Form Results", $msgbody, "From: client@hisdomain.com");

So how can you be careful? Well in my opinion the best way to be truely careful is to prevent anything that is associated with visitor input to go into your email headers. This way you have complete control and there are no security risks whatsoever. However that is not always how clients want it, they like to just be able to hit reply to an email and reply back to the potential customer. So therefore some extra security checks or spam traps as I like to call them, need to be in place.

Instead of writing out various methods of what to do there's a great page at ALT PHP FAQ that offers a really good script which you can use with very little editing required. All you need to do is alter the authHosts array to hold any domain that has permission to post to the script.

There are other methods and checks that can be made. More will be introduced in my PHP Learning category as and when I get to the functions involved. But for now secure your scripts as much as possible!

  1. 5 Responses to “PHP: Secure Form Mailing”

  2. And after badgering him, Khalid's finally written a post on the subject of secure forms too. The direct post can be read Here.

    By Sarah on Feb 24, 2006

  3. Unfortunately the script you point to relies on the HTTP Headers. Those cannot be trusted as well (like http_Referer, http_user_agent). So the method described is not as secure as one might think. Also, using a blacklist (bad strings) isn't the best approach. A much better and easier way in this case is using a function like ctype_print() to check for any newlines or carriage returns. So instead of all those lines of code you only need one: if(ctype_print($email))
    Of course a good regex to check the format of the email address should be used as well.

    By Matthijs on Feb 28, 2006

  4. When it comes down to it, the most suitable way is to hardcode the headers yourself as I first mentioned. However it's all a deterrent and that's the main point. Yes referrers, user agents etc can all be spoofed hence the addition of Khalid's post too.

    The ctype_print() function is another than could be used, something I read about from I believe your comment on Khalid's blog, so yes it's another option, however whilst it may save lines it only checks on newlines so therefore does the bare minimum and possibly doesn't check for other issues.

    On my own form I have other various fields being checked for things like a "http" being put into the telephone box, as spammers often just paste their url into every form box. If this is found the bot gets kicked to Google. There are a million and one checks that could and should be done, but when's all said and done, the more checks done the better and more secure it is. Whilst headers can be spoofed it's surely better to check them than not afterall, perhaps the person spamming is just wanting to spam you, so every other check is okayed. But lets say they have an automated script to post straight to the form mailer, but they didn't fix their headers. Thus despite every other check, the one that would stop that email would be the one I posted about. :)

    By Sarah on Feb 28, 2006

  5. Sarah, you're correct that hardcoding the headers is the easiest and in some cases the best way. And you're right to say that for every validation there are many options. But i just wanted to point to the fact that the http headers can be messed with because not many people know that (or at least not many beginners). It would be a pity if someone would think they were safe while in fact they're not.

    And in this specific case, dealing with email injection attempts, and your example (From: $name ), I think the best option is to pick a solid regex to make sure that $name contains only alphabetic characters (and maybe some others) and $email has a valid email format.

    On top of that use the ctype_print to check for newlines.

    So I'm not saying the methods mentioned in alt-php-faq are bad, but it's the general approach that I personally don't like. I would prefer to use one or two specific solid methods instead of 5 which each only work to some extent.

    By Matthijs on Feb 28, 2006

  1. 1 Trackback(s)

  2. Mar 11, 2008: Plans to Prosper » Blog Archive » PHP SQL Injection Attacks in Headers
Post a Comment

Please use your real name, nickname or an online name. Names I consider spam will be changed.