Error with PHP mail(): Multiple or malformed newlines found in additional_header
Asked Answered
B

10

42

Suddenly have started receiving the above error without any changes having been made to the script.

Host is 1and1 (I know...)

The script still works fine on a different server, and so my suspicion is that there must have been some server config change that has lead to this, although the hosts plead ignorance.

There's no information on the above error at all in Google that I can find - does anybody have any ideas? Server is running Apache if that helps.

Boohoo answered 17/6, 2015 at 9:31 Comment(1)
Where is your mail function code?Edson
S
64

Had just the similar problem.
It came out of the blue. No PHP Code was changed.

What was changed: PHP was upgraded 5.5.25-1 to 5.5.26.

A security risk in PHP mail() function has been fixed and extra newlines in additional_headers are allowed no more. Because extra newlines mean: now starts the email message (and we surely don't want somebody to inject some newlines through headers followed by an evil message).

What previously have worked fine, e.g. just having extra newlines after headers or even passing the whole message to additional_headers, will function no more.

Solution:

  • Sanitize your headers. No multiple newlines in additional_headers argument. These count as "multiple or malformed newlines": \r\r, \r\0, \r\n\r\n, \n\n, \n\0.
  • Use additional_headers for headers only. Email message (multipart or not, with ir without attachments, etc) belongs in message argument, not in headers.

PHP Security Bug report: https://bugs.php.net/bug.php?id=68776
C Code diff how its fixed: http://git.php.net/?p=php-src.git;a=blobdiff;f=ext/standard/mail.c;h=448013a472a3466245e64b1cb37a9d1b0f7c007e;hp=1ebc8fecb7ef4c266a341cdc701f0686d6482242;hb=9d168b863e007c4e15ebe4d2eecabdf8b0582e30;hpb=eee8b6c33fc968ef8c496db8fb54e8c9d9d5a8f9

Skirmish answered 17/6, 2015 at 17:3 Comment(2)
I had an extra trailing \n at the end of my additional headers... removing it solved the problem.Fricandeau
Great answer. Unfortunately, some old code uses \n\n in multipart my mail in extra headers. Could anybody provide a solution for that Situation?Chimerical
R
51

None of the above answers solved this problem for me. So, I expanded my search to "mail with attachment and HTML message issues." Piecing together info from a few different posts, I came up with this. It allows for BOTH HTML email and an attachment.

My original header code:

$header = "From: ".$from_name." <".$from_mail.">\r\n";
$header .= "Reply-To: ".$replyto."\r\n";
$header .= "MIME-Version: 1.0\r\n";
$header .= "Content-Type: multipart/mixed; boundary=\"".$uid."\"\r\n";
$header .= "--".$uid."\r\n";
$header .= "Content-Type: text/html; charset=ISO-8859-1\r\n";
$header .= "Content-Transfer-Encoding: 8bit\r\n";
$header .= $body."\r\n";
$header .= "--".$uid."\r\n";
$header .= "Content-Type: application/pdf; name=\"".$filename."\"\r\n"; 
$header .= "Content-Transfer-Encoding: base64\r\n";
$header .= "Content-Disposition: attachment; filename=\"".$filename."\"\r\n";
$header .= $content."\r\n";
$header .= "--".$uid."--";

if (mail($mail_to, $subject, "", $header))
{
    return "mail_success";
}
else
{
    return "mail_error";
}

My new code (complete): Note that $body is the HTML that is being assembled by a different function.

$file = $path.$filename;
$file_size = filesize($file);
$handle = fopen($file, "r");
$content = fread($handle, $file_size);
fclose($handle);

$content = chunk_split(base64_encode($content));
$uid = md5(uniqid(time()));
$name = basename($file);

$eol = PHP_EOL;

// Basic headers
$header = "From: ".$from_name." <".$from_mail.">".$eol;
$header .= "Reply-To: ".$replyto.$eol;
$header .= "MIME-Version: 1.0\r\n";
$header .= "Content-Type: multipart/mixed; boundary=\"".$uid."\"";

// Put everything else in $message
$message = "--".$uid.$eol;
$message .= "Content-Type: text/html; charset=ISO-8859-1".$eol;
$message .= "Content-Transfer-Encoding: 8bit".$eol.$eol;
$message .= $body.$eol;
$message .= "--".$uid.$eol;
$message .= "Content-Type: application/pdf; name=\"".$filename."\"".$eol;
$message .= "Content-Transfer-Encoding: base64".$eol;
$message .= "Content-Disposition: attachment; filename=\"".$filename."\"".$eol;
$message .= $content.$eol;
$message .= "--".$uid."--";

if (mail($mail_to, $subject, $message, $header))
{
    return "mail_success";
}
else
{
    return "mail_error";
}

Two key changes here. (1) removed all the multi-part stuff from the headers into $message. (2) removed all the "\r\n" stuff and added $eol = PHP_EOL; to the code.

Together, these changes allowed me to once again send HTML email with attachments.

Resupinate answered 4/8, 2015 at 14:37 Comment(5)
This worked, however I'm having issues sending multiple attachmentsClomp
Beware that before the content you must have a newline (newlines after last header) it should be... $message .= "Content-Disposition: attachment; filename=\"".$filename."\"".$eol.$eol; $message .= $content.$eol;Fraktur
Its solved my issue, But can you help me for multiple attachments?Herbie
still says "MIME-Version: 1.0\r\n";Choragus
For strict_types=1 you need to cast time() to a string for uniqid: $uid = md5(uniqid("".time()));.Hialeah
C
13

Had the same problem: Removed the mime boundary and message from the header and all worked.

    $header = "From: ".$from_name." <".$from_mail.">\n";
    $header .= "Reply-To: ".$replyto."\n";
    $header .= "MIME-Version: 1.0\n";
    $header .= "Content-Type: multipart/mixed; boundary=\"".$uid."\"\n\n";
    $emessage= "--".$uid."\n";
    $emessage.= "Content-type:text/plain; charset=iso-8859-1\n";
    $emessage.= "Content-Transfer-Encoding: 7bit\n\n";
    $emessage .= $message."\n\n";
    $emessage.= "--".$uid."\n";
    $emessage .= "Content-Type: application/octet-stream; name=\"".$filename."\"\n"; // use different content types here
    $emessage .= "Content-Transfer-Encoding: base64\n";
    $emessage .= "Content-Disposition: attachment; filename=\"".$filename."\"\n\n";
    $emessage .= $content."\n\n";
    $emessage .= "--".$uid."--";
    mail($mailto,$subject,$emessage,$header);
Chalcidice answered 17/6, 2015 at 16:55 Comment(5)
This was created by Fixed bug #68776 (mail() does not have mail header injection prevention for additional headers).Chalcidice
This works, but mail is not sent as HTML. HTML tags are shown in mail as it is. Any solution?Weatherspoon
@SunishMenon - I use - $headers .= 'Content-Type: text/HTML; charset=ISO-8859-1' . "\r\n"; $headers .= 'Content-Transfer-Encoding: 8bit'. "\n\r\n"; see my answer bellowTurnbuckle
I was using the mailing function posted here, so I updated the answer to reflect the splitting of header and message that @Chalcidice here doesShillelagh
I also needed to drop the double \n after the first Content-Type, but it might be PHP7-specificShillelagh
C
5

None of the above fixed it for me - main issue is you must not put anything other than header definitions in headers. Old scripts bunged anything in there. So move any text or attachments that were stuffed in to headers into the message body. Makes sense..

This has an explanation
(I guess it's same solution as Frank's above plus Davisca's "no double new lines" - but you need doubled new lines for attachments)

Customer answered 24/7, 2015 at 10:16 Comment(0)
T
3

This will solve your problem. I have changed a little bit of Frank's code. This code will support attachment and html.

 <?php

$filename  = "certificate.jpg";
$path      = "/home/omnibl/subdomains/test/certificate/certimage/";
$file      = $path . $filename;
$file_size = filesize($file);
$handle    = fopen($file, "r");
$content   = fread($handle, $file_size);
fclose($handle);

$content = chunk_split(base64_encode($content));
$uid     = md5(uniqid(time()));
$name    = basename($file);

$eol     = PHP_EOL;
$subject = "Mail Out Certificate";
$message = '<h1>Hi i m mashpy</h1>';

$from_name = "[email protected]";
$from_mail = "[email protected]";
$replyto   = "[email protected]";
$mailto    = "[email protected]";
$header    = "From: " . $from_name . " <" . $from_mail . ">\n";
$header .= "Reply-To: " . $replyto . "\n";
$header .= "MIME-Version: 1.0\n";
$header .= "Content-Type: multipart/mixed; boundary=\"" . $uid . "\"\n\n";
$emessage = "--" . $uid . "\n";
$emessage .= "Content-type:text/html; charset=iso-8859-1\n";
$emessage .= "Content-Transfer-Encoding: 7bit\n\n";
$emessage .= $message . "\n\n";
$emessage .= "--" . $uid . "\n";
$emessage .= "Content-Type: application/octet-stream; name=\"" . $filename . "\"\n"; // use different content types here
$emessage .= "Content-Transfer-Encoding: base64\n";
$emessage .= "Content-Disposition: attachment; filename=\"" . $filename . "\"\n\n";
$emessage .= $content . "\n\n";
$emessage .= "--" . $uid . "--";
mail($mailto, $subject, $emessage, $header);
Trescott answered 13/8, 2015 at 5:28 Comment(1)
Great... Just had to fix this in a 5.4.32 to 5.4.40 php migrationHibernicism
R
2

Another scenario that brings the same new error is if you are not sending any headers to the "mail" command. It used to just use a default, and now gives the misleading error: "Multiple or malformed newlines found in additional_header".

Can be fixed by adding this:

$header = "From: ".$from_name." <".$from_mail.">\n";
$header .= "Reply-To: ".$replyto."\n";
$header .= "MIME-Version: 1.0\n";

...

mail($mailto,$subject,$emessage,$header);
Recreation answered 3/7, 2015 at 19:15 Comment(0)
T
2

my PHP version - 5.4.43, probably contains Fixed bug #68776.

googling to the same error showed [http://fossies.org/diffs/php/5.4.42_vs_5.4.43/ext/standard/mail.c-diff.html]

=> I cannot use empty strings as mail() parameters.

my old code:

$headers = 'From: ' . $frm . "\r\n";
$headers .= 'To: ' . $contactEmail . "\r\n";
if ( $flag ) {
  $headers .= 'To: ' . $contactEmail2 . "\r\n";
}
$headers .= 'Cc: ' . $contactEmailCc . "\r\n";
$headers .= 'Bcc: ' . $contactEmailBcc . "\r\n";
$headers .= 'Return-Path: ' . $frm . "\r\n";
$headers .= 'MIME-Version: 1.0' ."\r\n";
$headers .= 'Content-Type: text/HTML; charset=ISO-8859-1' . "\r\n";
$headers .= 'Content-Transfer-Encoding: 8bit'. "\n\r\n";

$headers .= $htmlText . "\r\n";

if (!mail('', $strSubject, '', $headers)) {    // !!! note the empty parameters.

my new code:

$headers = 'From: ' . $frm . "\r\n";
// note: no "To: " !!!
$headers .= 'Cc: ' . $contactEmailCc . "\r\n";
$headers .= 'Bcc: ' . $contactEmailBcc . "\r\n";
$headers .= 'Return-Path: ' . $frm . "\r\n";
$headers .= 'MIME-Version: 1.0' ."\r\n";   
$headers .= 'Content-Type: text/HTML; charset=ISO-8859-1' . "\r\n";
$headers .= 'Content-Transfer-Encoding: 8bit'. "\n\r\n";
// note: no $htmlText !!!

// note: new parameters:  
$mTo = $contactEmail;
if ( $flag ) {
  $mTo .= ', ' . $contactEmail2;
}

$mMessage .= $htmlText . "\r\n";

if (!mail($mTo, $strSubject, $mMessage, $headers)) {
Turnbuckle answered 28/7, 2015 at 8:53 Comment(0)
A
1

You may be running into Bug #69874 Can't set empty additional_headers for mail() if you haven't done anything stupid (i.e. forgot to sanitize the headers).

Test for the bug

$ php -d display_errors=1 -d display_startup_errors=1 -d error_reporting=30719 -r 'mail("[email protected]","Subject Here", "Message Here",NULL);'

Warning: mail(): Multiple or malformed newlines found in additional_header in Command line code on line 1

Alternately if you know your PHP version (hint: php -v) you can check the changelog for the bug number (69874) to see whether the fix has been applied for your version.

A short-term fix is to replace calls to mail() like this

 function fix_mail(  $to ,  $subject ,  $message , $additional_headers =NULL,  $additional_parameters=NULL ) {
            $to=filter_var($to, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES| FILTER_FLAG_STRIP_LOW| FILTER_FLAG_STRIP_HIGH);
            $subject=filter_var($subject, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES| FILTER_FLAG_STRIP_LOW| FILTER_FLAG_STRIP_HIGH);             

            if (!$additional_headers)
                    return mail(  $to ,  $subject ,  $message );

            if (!$additional_parameters)
                     return mail(  $to ,  $subject ,  $message , $additional_headers );

            return mail(  $to ,  $subject ,  $message , $additional_headers, $additional_parameters );
    }
Ancipital answered 6/7, 2015 at 14:39 Comment(0)
P
1

Incase this helps anyone, I am using PHP 5.6 and an old code igniter v1 email library

email.php: line 1510 - I've added this:

     $this->_header_str = str_replace("\r\r","",$this->_header_str);
                    $this->_header_str = str_replace("\r\0","",$this->_header_str);
                    $this->_header_str = str_replace("\r\n\r\n","",$this->_header_str);
                    $this->_header_str = str_replace("\n\n","",$this->_header_str);
                    $this->_header_str = str_replace("\n\0","",$this->_header_str);

above this line:

 if ( ! mail($this->_recipients, $this->_subject, $this->_finalbody, $this->_header_str, "-f ".$this->clean_email($this->_headers['Return-Path'])))
                            return FALSE;
                    else
                            return TRUE;

And that is successfully sanitising the email headers and resolving the error that I was receiving (same as the original poster of this question)

Pantalets answered 30/9, 2020 at 23:27 Comment(0)
K
0

This is quite possibly someone trying to take advantage of your code to inject email headers.

http://resources.infosecinstitute.com/email-injection/

I would suggest you examine access logs etc and look for unusual activity. The fact you are getting error messages hopefully means that your script has not been compromised and it is erroring out instead. You need to make sure though.

Kaylil answered 17/6, 2015 at 10:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.