Anyone who has an email address can tell you that spam is one of the great banes of the online world. But it’s not only distant servers owned by the spammers that are to blame. It may even be your very own server. Insecure PHP scripts have provided great opportunities for spammers to abuse other’s resources to send out their spam. In particular, it’s the mail() function that can be abused. I myself was the target a few months ago when I noticed spam being sent from an old form on my server that I’d forgotten about. This month’s article looks at techniques that can be used to harden your mail form, and reduce the chances of it being misused.
How do the spammers find you?
There are two main ways they find you. One of course, is by visiting your site, and following the links. If you have a publicly available contact form, there’s not much you can do about this one. The other, surprisingly common, way is through a search engine such as Google. By googling for mail.php, contact.php, and the like, spammers will get a list of likely targets. You can reduce this likelihood by choosing an unusual name. This sort of security by obscurity is certainly not anywhere near sufficient, but I’m a fan as, all else being equal, the less attempts, the less chance of an exploit. You of course can’t stop people googling for words such as contact on your web page, which should then lead to your script, but for spammers googling for mail.php and so on it is preferable, as with this one effort, they are able to identify a mail form and a PHP script–and they’re fairly certain that the developer hasn’t read this article!
Here’s a simple HTML form used for sending mail, followed by the script for sending it. It’s stripped to its bare minimum for now.
form.html (1)
<form action='mail.php' method='post'> Email: <input type='text' name='email'><br> Mail body: <textarea name='body'></textarea><br> <input type='submit' value='Send comments'> </form>
mail.php (1)
<?php $to = "bob@domain_example.co.za"; $subject = "Email from website"; $message = $_REQUEST["body"]; $email = $_REQUEST["email"]; $headers = "From: $email"; mail($to, $subject, $message, $headers); echo "Thanks for submitting."; ?>
Here’s what the results should look like (you can view the message source on your email client to see, with the exception of any bcc field, exactly what was sent).
To: bob@domain_example.co.za Subject: Email from website From: sender@their_domain.co.za
However, this form and script combination is a spam relay waiting to happen. Notice that the to address is hard-coded. Many applications allow the user to specify the to address, and this is of course opens the door even further. However, too many people naively assume that hard-coding it is sufficient to avoid spam. This is certainly not true. The script above uses $_REQUEST, which accepts both $_POST and $_GET variables. This is simply to ease the exploit even further for demonstration purposes. It’s best to specify $_POST (in the case of our form’s example), but limiting a potential spammer to $_GET or $_POST will at best cause an insignificant delay.
Exploiting the script
Assuming the above form and script sit on a server called your-domain.co.za, spammers can simply send the following request:
http://your-domain.co.za/mail.php?body=gotcha&[email protected]%0Abcc:[email protected],[email protected]
Let’s look at this in more detail. The variable names are available in the form, so the spammer knows to use body and email. The body will contain the contents of the mail (in this case just the single word, gotcha), though of course in practice it would be cheap deals and viagra and the offers of millions if you just supply your bank account details to the ex-wife of some or other dictator.
Next comes the the sneaky bit. The email field, which supposedly just contains the sender’s email (in this case [email protected] is used), also contains a bcc field, followed by a comma-delimited list of emails to be spammed. The %0A character is a linefeed.
Here’s what the email headers would look like.
To: bob@domain_example.co.za Subject: Email from website From: [email protected] Bcc: [email protected],[email protected]
Without any protection, it’s a fairly simple matter for a mail form to be abused. So how can we protect against this? There are a number of things to do, and a number of spammer responses, so I suggest reading through the whole article carefully before making any changes.
Checking for valid email
Since the email field was used to mask the bcc list, and it makes good sense to avoid receiving comments from people who don’t bother to put a valid email in, one way to stop this is to test for a valid email. This is not enough, but it’s a step closer towards the principle of not trusting any values that the user can edit. The changes are in bold:
mail.php (2)
<?php $to = "bob@domain_example.co.za"; $subject = "Email from website"; $message = $_REQUEST["body"]; $email = $_REQUEST["email"]; function is_valid_email($email) { return preg_match('#^[a-z0-9.!#$%&'*+-/=?^_`{|}~]+@([0-9.]+|([^s]+.+[a-z]{2,6}))$#si', $email); } if (!is_valid_email($email)) { echo 'Sorry, invalid email'; exit; } $headers = "From: $email"; mail($to, $subject, $message, $headers); echo "Thanks for submitting."; ?>
It’s too soon to be complacent. Let’s change the example slightly by adding a subject field:
form.html (2)
<form action='mail.php' method='post'> Email: <input type='text' name='from_email'><br> Subject: <input type='text' name='subject'><br> Mail body: <textarea name='body'></textarea><br> <input type='submit' value='Send comments'> </form>
mail.php (3)
<?php $to = "bob@domain_example.co.za"; $subject = "Email from website"; $message = $_REQUEST["body"]; $subject = $_REQUEST["subject"]; $email = $_REQUEST["email"]; function is_valid_email($email) { return preg_match('#^[a-z0-9.!#$%&'*+-/=?^_`{|}~]+@([0-9.]+|([^s]+.+[a-z]{2,6}))$#si', $email); } if (!is_valid_email($email)) { echo 'Sorry, invalid email'; exit; } $headers = "From: $email"; mail($to, $subject, $message, $headers); echo "Thanks for submitting."; ?>
Once again this can be exploited, with something like:
http://domain_example.co.za/tests/mail3.php?body=gotcha&[email protected]&subject=GETVIAGRA%0Acc:[email protected],[email protected]