Proper Prevention of Mail Injection in PHP

Proper prevention of mail injection in PHP

To filter valid emails for use in the recipient email field, take a look at filter_var():

$email = filter_var($_POST['recipient_email'], FILTER_VALIDATE_EMAIL);

if ($email === FALSE) {
echo 'Invalid email';
exit(1);
}

This will make sure your users only supply singular, valid emails, which you can then pass to the mail() function. As far as I know, there's no way to inject headers through the message body using the PHP mail() function, so that data shouldn't need any special processing.

Update:

According to the documentation for mail(), when it's talking directly to an SMTP server, you will need to prevent full stops in the message body:

$body = str_replace("\n.", "\n..", $body);

Update #2:

Apparently, it's also possible to inject via the subject, as well, but since there is no FILTER_VALIDATE_EMAIL_SUBJECT, you'll need to do the filtering yourself:

$subject = str_ireplace(array("\r", "\n", '%0A', '%0D'), '', $_POST['subject']);

Is this mail() function safe from header injection?

Header injection relies on being able to insert additional newlines into header variables, which makes the string look like a new header.

For example, allowing a subject value of Testing\nCc: spamrecipient@example.com\n\nSome body text would result in a message header containing:

Subject: Testing
Cc: spamrecipient@example.com

Some body text

i.e. the abuser has not only added additional recipients, but they've managed to supply their own body text too.

However in your case the $toaddress is constant, and even if $toaddress had been user-supplied it should be correctly sanitised by the mail() function.

Your subject header is similarly constant

The $message variable is safe because by definition that's the body text and only sent after the real headers.

That only leaves $fromaddress, and you're already using FILTER_VALIDATE_EMAIL on that which should also reject anything with a newline in it.

However you should strictly be checking the result of that test, and aborting the whole thing if the result is FALSE. As it is if the validation fails then mail() will complain about being given a blank From: address, but there's no header injection opportunity there.

As far as I can tell, then, this code is actually secure.


Also, IMHO, you shouldn't send the emails from the user-supplied email address. That would fall foul of anti-spam mechanisms such as SPF.

You should use a constant From: value belonging to your own domain. If you like you could then use a correctly sanitised value in the Reply-To header to make it easier to have the subsequent reply go to the desired address.

Is there any injection vulnerability in the body of an email?

There's a possible injection in the body text if you're speaking native SMTP to the mail server.

A single . on its own terminates the current body in SMTP, so in theory you could have user supplied input like this:

some body text
.
MAIL FROM: <...>
RCPT TO: <...>
DATA
Subject: here's some spam

here's a new body

and the SMTP server might allow the second message through.

Some SMTP servers can be configured to prevent this by not allowing SMTP commands to be pipelined (i.e. requiring the client to read the response before permitting the next command).

What protection is needed for PHP mail scripts?

If you're not using a database then no, you don't need to protect against attacks that exploit database queries. Emails have a whole set of exploits of their own and I recommend using a library such as phpmailer or swiftmailer which will help with this. Either way, you should always verify that the data submitted from the form is in the format you expect it to be.

Preventing mail injection in php intercepting some characters \r, \n, %0A, %0D

I did a test. The test result is a success! I tested the regex trying directly in localhost with different methods:

<?php
$test = "the dog \n was \r sleeping on the floor";
if (preg_match_all('/(%0A|%0D|\\n+|\\r+|;|mime-version:|content-type:|content-transfer-encoding:|subject:|to:|cc:|bcc:)/i', $test, $tmp)) {
echo "I found this character: '";
print_r($tmp[1]);
echo "'";
} else {
echo "I cannot find any string searched";
}
?>

Result:

I found this character: 'Array ( [0] => [1] => ) '

Looking at source I can see the \n and the \r

I found this character: 'Array
(
[0] =>

[1] =>
)
'

So I think that the regex is well build.

Also other test I did with strpos():

if !(strpos($_POST['subject'],'\n') === false)) {

fails with single quotes while finds the \n with double quotes...

if !(strpos($_POST['subject'],"\n") === false)) {

Conclusions: regex is well formed and strpos() needs "" to match \n or \r.

Are to, subject and body safe against injection using mail()?

Yes, that's true, but it's also incomplete. In the engine source code, the function php_mail_build_headers ensures headers comply with RFC 2822 § 3.6 requirements for maximum number of values. Particularly, the following headers are checked for single value:

  • orig-date
  • from
  • sender
  • reply-to
  • to
  • bcc
  • message-id
  • in-reply-to
  • subject

Yes, the message parameter is safe from header injection by definition: the message part is inserted after the separating new line between headers and body, so any header-like text inserted as part of the message will appear as literal text within the message body.

How to prevent code injection attacks in PHP?

  • mysql_real_escape_string used when insert into database
  • htmlentities() used when outputting data into webpage
  • htmlspecialchars() used when?
  • strip_tags() used when?
  • addslashes() used when?

htmlspecialchars() used when?

htmlspecialchars is roughly the same as htmlentities. The difference: character encodings.

Both encode control characters like <, >, & and so on used for opening tags etc. htmlentities also encode chars from other languages like umlauts, euro-symbols and such. If your websites are UTF, use htmlspecialchars(), otherwise use htmlentities().

strip_tags() used when?

htmlspecialchars / entities encode the special chars, so they're displayed but not interpreted. strip_tags REMOVES them.

In practice, it depends on what you need to do.

An example: you've coded a forum, and give users a text field so they can post stuff. Malicious ones just try:

pictures of <a href="javascript:void(window.setInterval(function () {window.open('http://evil.example');}, 1000));">kittens</a> here

If you don't do anything, the link will be displayed and a victim that clicks on the link gets lots of pop-ups.

If you htmlentity/htmlspecialchar your output, the text will be there as-is. If you strip_tag it, it simply removes the tags and displays it:

pictures of kittens here

Sometimes you may want a mixture, leave some tags in there, like <b> (strip_tags can leave certain tags in there). This is unsafe too, so better use some full blown library against XSS.

addslashes

To quote an old version of the PHP manual:

Returns a string with backslashes before characters that need to be quoted in database queries etc. These characters are single quote ('), double quote ("), backslash () and NUL (the NULL byte).

An example use of addslashes() is when you're entering data into a database. For example, to insert the name O'reilly into a database, you will need to escape it. It's highly recommeneded to use DBMS specific escape function (e.g. mysqli_real_escape_string() for MySQL or pg_escape_string() for PostgreSQL), but if the DBMS you're using does't have an escape function and the DBMS uses \ to escape special chars, you can use this function.

The current version is worded differently.



Related Topics



Leave a reply



Submit