When working with new programmers, and sometimes even experienced ones, I found that most of the time they would know how to solve a problem and code its solution but they wouldn’t be aware of security related implications. In this article, I’ll show how to improve security on PHP forms that use mail().
For a very simple Web site, a common usage of PHP is to create a contact form that will received visitor’s information and send it thru mail to the responsible of the Web site. The solution to this task is quite easy: create a form on HTML which will do a POST of the fields to a PHP script, and on the script use PHP’s mail() function to send the email with the provided information. If you want to have a more flexible solution, you could even receive on the script the recipient’s address, subject or some headers for the message.
Well, before going ahead and creating this simple form submission script (or you already have placed already on a Web site?), note that the script might be subject to a exploit which will allow spammers to send hundreds or thousands of messages without you being aware of it!. Please also note that even a proper anti-relay configuration of your MTA will not solve this problem since the messages are in fact valid locally-generated messages.
What happens is that a spammer, after some examination of your form’s fields, will prepare a POST request that contains specially crafted fields that will include new recipients (this process is called injection and is not limited to mail()). How is this possible? For this particular case, the spammer will include new “valid headers” on the POST that will trick mail() to send the message as Cc: or Bcc: with the victim’s addresses as new lines on the POST (separated with LF – line feed). The spammer could also include different MIME sections to hide the original form submission data and show only his message on the victim’s email agent.
Here’s a sample of how our message’s content would look like after being prepared by a spammer:
[XX-remitente_email] => namin
Content-Type: text/plain; charset=\”us-ascii\”
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: a quarter past twelve and
bcc: batts1005@example.com
0e572d2671fa1c41bfcb3c522f68738a
.
With XX-remitente_email passed to mail() as the body, an email originated on your server would be sent to batts1005@example.com.
The solution? Sanitize all the received data that is going to be used as mail()’s parameters, stripping away all the LF and CR characters that appear on the data:
Original code
[php]
if (mail($recipient, $subject, $_POST[\'body\'], \”From: \” . $_POST[\'mail\'] . \”\\r\\nReply-To: \” . $_POST[\'mail\'])) {
$sent = true;
} else {
$sent = false;
}
[/php]
Sanitized code
[php]
$_POST[\'body'] = stripCRLF($_POST[\'body\']);
$_POST[\'mail\'] = stripCRLF($_POST[\'mail\']);
if (mail($recipient, $subject, $_POST[\'body'], \”From: \” . $_POST[\'mail\'] . \”\\r\\nReply-To: \” . $_POST[\'mail\'])) {
$sent = true;
} else {
$sent = false;
}
function stripCRLF($param) {
$param = preg_replace(\”/\\r/\”, \”\”, $param);
$param = preg_replace(\”/\\n/\”, \”\”, $param);
return $param;
}
[/php]
While programming Web applications, it is always a good practice to never trust the input from a visitor as a parameter for a procedure, function or query since it could be a specially crafted value trying to take advantage of a weakness or bug, either on your code or on the language’s native functions. One should always filter and validate all the raw data received from any Internet user that is going to be used somehow in our code.
The same considerations should be applied to any data used in SQL queries, system calls which use visitor’s input as parameters and variables that could be used without being previously declared or sanitized.
More information
Leave a Reply
You must be logged in to post a comment.