|
Now you have seen the problem and how the php mail() works, it's the time for security.
The first - and obvious - solution to the php mail() injection is to use another mailer such as zend_mail and pear mail to send the messages. Buy if you already have a script coded to use the php mail(), this solution isn't handy.
Another unhandy and common solution you may find while browsing on this security issue is to install mod_security on your server and tell it to scan the POST and GET for bcc:, cc: and to: an reject these requests. If you choose this solution, add the following rule to your mod_security setup:
SecFilterSelective ARGS_VALUES "\n[[:space:]]*(to|bcc|cc)[[:space:]]*:.*@"
Our favorite solution consists on using regular expressions (regexps) to filter the form data in association of registered fields. First, let's take a look on some good examples on how regexps can be used to stop e-mail injection. Below you'll find some examples of regexp filters and a description of what each one does. Change $field to the name of the field you'd like to filter.
Stop if a form contains any occurrence of "\r" or "\n".
if (eregi("(\r|\n)", $field)) {
die("Get out, spammer.");
}
Stop if the format of the e-mail address is invalid.
if(!eregi("^[a-z0-9]+([_\\.-][a-z0-9]+)*" ."@"."([a-z0-9]+([\.-][a-z0-9]+)*)+"."\\.[a-z]{2,}"."$",$field))
{
die("Invalid e-mail address.");
}
Stop if there is an @ (at) on the field.
if(eregi("@",$field)){
die("Invalid content for this field.");
}
Seems easy to implement? It really is.
If you use regexps to filter your scripts in association of registered fields, your script will be injectionproof. To register the fields of your form, follow two easy steps.
Step 1: Create a file with the following data to be included by your script. In our example, the name of the file will be "stopinjection.inc.php".
<?php
function regfld()
{
$num_args = func_num_args();
$vars = array();
if ($num_args >= 2) {
$method = strtoupper(func_get_arg(0));
if (($method != 'SESSION') && ($method != 'GET') && ($method != 'POST') && ($method != 'SERVER')
&& ($method != 'COOKIE') && ($method != 'ENV')) {
die('The first argument of regfld must be one of the following: GET, POST, SESSION, SERVER,
COOKIE, or ENV');
}
$varname = "HTTP_{$method}_VARS";
global ${$varname};
for ($i = 1; $i < $num_args; $i++) {
$parameter = func_get_arg($i);
if (isset(${$varname}[$parameter])) {
global $$parameter;
$$parameter = ${$varname}[$parameter];
}
}
} else {
die('You must specify at least two arguments');
}
}
?>
Step 2: Now add the following lines to your script. This code will check if at least two fields have been filled, if the fields were filled with valid data and if the HTTP method is valid. At the end of the following code there is a calling to stripslashes(), that filters any slashes that may have been used in the form in attempts to inject data. Only after these verification procedures the script continues to the sending mail routine.
include("stopinjection.inc.php");
$errors=0;
$error="The following errors were found while processing your request.<ul>";
regfld('POST','field');
regfld('POST','field2');
$msg=preg_replace("/(\015\012)|(\015)|(\012)/"," ", $msg);if($field=="" || $field2=="" ){
$errors=1;
$error.="<li>You didn't fill all the fields.";
if($errors==1) echo $error;
else{
$where_form_is=
"http".($HTTP_SERVER_VARS["HTTPS"]=="on"?"s":"")."://".$SERVER_NAME.strrev(strstr(strrev($PHP_SELF),"/"));
$message="Field 1: ".$field."
Field 2: ".$field2."
";
$message = stripslashes($message);
mail(); // Use your mail() parameters here.
}
When you use one or more of the described forms to protect your script from e-mail injection, you will be also helping Internet to stay free of spam. Even if you haven't been a victim of e-mail injection, always try to keep your scripts and server the most secure as possible.
|