Email injection attacks—what they are and how to avoid them
Email injection is a technique that has been known for some time, but a rash of attacks starting around August 2005 indicated that someone had found a way of automating the process. The attack is designed to exploit a particular feature of PHP, but the automated attack appears to target all online forms, not just those written using PHP.
What an attack looks like
The first indication that you have been a victim of an injection attack is the arrival of an email filled with repeated instances of a fictitious email address using the same domain name as the site where the form is located.
Name: abcdefgh1234@example.com
Address:
abcdefgh1234@example.com
Email:
abcdefgh1234@example.com
There will also be a Bcc: in there, with a different address. That's where the attacker has sent a copy of the message to see whether the attack succeeded. If it did, the next step is to turn your website into an unwitting spam distributor
What the attack is trying (or succeeding) to do
The PHP mail() function is designed to be very easy to use. It requires just three arguments containing the following information: the address the mail is to be sent to, a subject line, and the content of the message. A fourth, optional argument allows you to specify other email headers, such as From:, Cc:, Bcc:, and so on.
Many PHP mail scripts use this fourth argument to insert the sender's email address into the From: header. It's a very useful technique, because it means that any email arriving from your online form has the sender's address in the From: field. All you have to do is to hit the reply button in your email program, and the reply should go straight to the sender—assuming the sender has provided a genuine email address, that is...
Instead of a genuine email address, an injection attack sends additional headers which hijack the form to send email to hundreds or thousands of other addresses. In effect, your innocent feedback form becomes a spam relay.
The problem—failure to check user input
Many website designers are either too lazy—or don't have sufficient knowledge of server-side languages—to check user input. Neither is acceptable. If you put server-side code, such as PHP, ASP, or ColdFusion, on a website, you should have sufficient knowledge of how it works to perform simple validation of user input. If you are relying on JavaScript validation, it's not enough.
- JavaScript validation of user input is useless if JavaScript is disabled in the attacker's browser.
- Validation must also be carried out in your server-side mail script.
A simple solution
The exploit relies on additional headers being passed to the PHP mail() function through one of the input fields of an online form. Each additional header must be separated by a line feed and carriage return. The exploit also aims to send a long list of email addresses, which will be separated either by commas or semicolons, and surrounded by quotes. None of these characters are allowed in a genuine email address, so the solution is to refuse to send email if any of them are detected in a field that should contain only an email address.
The following Perl-Compatible Regular Expression (PCRE) detects the presence of any of the problem characters:
/[\r\n,;\'"]/
This is one suggested way that you could use it in a PHP mail() script:
if (preg_match(' /[\r\n,;\'"]/ ', $_POST['email'])) {
exit('Invalid email address');
}
else {
//code to send the mail
}
A more elegant solution would redirect the user to an error page or simply redisplay the form with an error message. The important thing is that no mail should be sent if the illegal characters are detected in an email field.
You should, in fact, carry out other validation checks, but this should provide a solid line of defence against injection attacks. Although this solution is PHP-specific, a similar technique could be adopted for ASP and ColdFusion to prevent attackers' test emails clogging up your inbox.
So, does this mean PHP has a security gap?
Perhaps, but the real security gap lies in poor quality scripting. Since the release of PHP 4.2.3 in September 2002, system administrators have been able to turn off the use of the optional fourth parameter in mail() by running PHP in safe_mode. The spate of attacks in late 2005 has prompted some hosting companies to adopt this solution. Unfortunately, the use of additional headers is vital in some circumstances, such as multilingual sites that need to use UTF-8 or other encoding for email.
The switch to safe_mode is a step in the wrong direction, albeit an understandable one on the part of hosting companies. It's web designers who should run in "safe mode" by understanding better what they're doing when using any server-side language.
