Below is the code I'm using for a simple contact form. It seems our code is being manipulated and someone is using the contact form for email injection. I'm relatively new to PHP and I've tried researching online but currently I'm having no joy.
Does anyone have some advice?
<?php
// get posted data into local variables
$EmailFrom = Trim(stripslashes($_POST['EmailFrom']));
$EmailTo = "email@email.com";
$Subject = "subject";
//$Title = Trim(stripslashes($_POST['Title']));
$First = Trim(stripslashes($_POST['First']));
//$Surname = Trim(stripslashes($_POST['Surname']));
//$Company = Trim(stripslashes($_POST['Company']));
//$Address = Trim(stripslashes($_POST['Address']));
//$Address2 = Trim(stripslashes($_POST['Address2']));
//$Address3 = Trim(stripslashes($_POST['Address3']));
//$Area = Trim(stripslashes($_POST['Area']));
//$County = Trim(stripslashes($_POST['County']));
//$Postcode = Trim(stripslashes($_POST['Postcode']));
$Telephone = Trim(stripslashes($_POST['Telephone']));
//$Fax = Trim(stripslashes($_POST['Fax']));
$EmailFrom = Trim(stripslashes($_POST['EmailFrom']));
$AmountOwed = Trim(stripslashes($_POST['AmountOwed']));
$ip = Trim(stripslashes($_POST['ip']));
//$Marketing = Trim(stripslashes($_POST['Marketing']));
//$Contact = Trim(stripslashes($_POST['Contact']));
$Details = Trim(stripslashes($_POST['Details']));
// validation
$validationOK=true;
if (Trim($EmailFrom)=="Your email: (required)") $validationOK=false;
if (!$validationOK) {
print "<meta http-equiv=\"refresh\" content=\"0;URL=error.php\">";
exit;
};
if (Trim($Telephone)=="Your Telephone: (required)") $validationOK=false;
if (!$validationOK) {
print "<meta http-equiv=\"refresh\" content=\"0;URL=error.php\">";
exit;
};
if (Trim($First)=="Your name: (required)") $validationOK=false;
if (!$validationOK) {
print "<meta http-equiv=\"refresh\" content=\"0;URL=error.php\">";
exit;
}
// prepare email body text
$Body = "";
//$Body .= "Title: ";
//$Body .= $Title;
//$Body .= "
";
$Body .= "First: ";
$Body .= $First;
$Body .= "
";
//$Body .= "Surname: ";
//$Body .= $Surname;
//$Body .= "
";
//$Body .= "Company: ";
//$Body .= $Company;
//$Body .= "
";
//$Body .= "Address: ";
//$Body .= $Address;
//$Body .= "
";
//$Body .= "Address2: ";
//$Body .= $Address2;
//$Body .= "
";
//$Body .= "Address3: ";
//$Body .= $Address3;
//$Body .= "
";
//$Body .= "Area: ";
//$Body .= $Area;
//$Body .= "
";
//$Body .= "County: ";
//$Body .= $County;
//$Body .= "
";
//$Body .= "Postcode: ";
//$Body .= $Postcode;
//$Body .= "
";
$Body .= "Telephone: ";
$Body .= $Telephone;
$Body .= "
";
//$Body .= "Fax: ";
//$Body .= $Fax;
//$Body .= "
";
$Body .= "EmailFrom: ";
$Body .= $EmailFrom;
$Body .= "
";
$Body .= "AmountOwed: ";
$Body .= $AmountOwed;
$Body .= "
";
$Body .= "ip: ";
$Body .= $ip;
$Body .= "
";
//$Body .= "Marketing: ";
//$Body .= $Marketing;
//$Body .= "
";
//$Body .= "Contact: ";
//$Body .= $Contact;
//$Body .= "
";
$Body .= "Details: ";
$Body .= $Details;
$Body .= "
";
// send email
$success = mail($EmailTo, $Subject, $Body, "From: <$EmailFrom>");
// redirect to success page
if ($success){
print "<meta http-equiv=\"refresh\" content=\"0;URL=thankyou.php\">";
}
else{
print "<meta http-equiv=\"refresh\" content=\"0;URL=error.php\">";
}
?>
I would start by looking over http://securephpwiki.com/index.php/Email_Injection#Using_php_mail.28.29_function to get an idea of what's actually happening.
On the input, validate that the inputs, for example check the input email is actually an email address:
if (!filter_var($EmailFrom, FILTER_VALIDATE_EMAIL)) {
// This isn't actually an email address
}
I suggest you to use a (simple?) regular expression to validate all the fields you accept from online modules.
Just as an example, you can check if a email address is conform to RFC822 using something like
if (!preg_match('^\S+@\S+\.\S+$/',$EmailFrom)) { # this is bad
You can also use this specific PECL function for that specific task: http://php.net/manual/en/function.mailparse-rfc822-parse-addresses.php
The point is to avoid accepting control characters (e.g. newline) from the attacker, so define a strict regex and validate ALL the input against known patterns.
PHP's mail function is highly vulnerable to attack. There are a number of vectors to be aware of, but the most likely one is a header injection:
$success = mail($EmailTo, $Subject, $Body, "From: <$EmailFrom>");
In the code above, you are specifying the headers
field as "From: <$EmailFrom>"
.
If $EmailFrom
contains a valid email address and nothing else, then this is perfectly fine. However, all a hacker needs to do is append line feeds to it, and he can start adding additional headers. And once you can insert arbitrary headers into an email, you can basically rewrite the entire email.
You currently aren't doing any kind of validation on the email address, so it would be trivially easy for a hacker to inject whatever he liked into this field.
So the short answer is to be much more careful to validate that the email addresses being submitted are actually valid email addresses.
However, as I said already, this isn't the only problem with PHP's mail()
function, so longer term you should seriously consider replacing it with a more robust solution such as the phpMailer or SwiftMailer libraries. These libraries resolve the security issues with sending mail in PHP by adding a strong layer of security features. They also make it a lot easier to use more advanced email features like html email and attachments.
The other thing you should do if you're having a security issue in PHP is make sure that you are using an up-to-date version. If your PHP version is anything lower than 5.4.45 (as of the date of this answer), then you should consider it to have known security holes which could be expolited, regardless of how good your actual code is. You haven't mentioned what version you're on, but please check and consider upgrading if necessary.
Finally, I note that you are doing trim(stripslashes(...))
on your input data. Please note that this is completely useless. Newer versions of PHP do not need you to do this at all. Older versions of PHP had a setting that automatically added slashes to input data; stripslashes()
was then used in your code to remove them. However this feature was removed from PHP a number of years ago, so if you're using a recent PHP version the stripslashes()
function will not be achieving anything. But more significantly, even when it was necessary, it certainly doesn't do anything to validate the content of the field or make it safe from attack.