-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix DKIM issue in 6.1.2 - update PHPMailer.php #1962
Conversation
1557: removed the last static::$LE, $header_dkim has already one 4479: it did not work when ($this->Mailer != 'smtp') AND (not on Windows) cause EOL in $signHeader is "\n" and not "\r\n"
@@ -1554,7 +1554,7 @@ public function preSend() | |||
$this->MIMEBody | |||
); | |||
$this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . static::$LE . | |||
static::normalizeBreaks($header_dkim) . static::$LE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we double check whether this would be better fixed by having $header_dkim
not include an LE to start with, which is done on line 4671?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking further into this - PHPMailer generally doesn't trust its own assembly of headers, and pretty much always strips breaks and readds them, for example in smtpSend
and getSentMIMEMessage()
. That said, MIMEHeader
is expected to have a trailing break as it often gets mailHeader
concatenated directly to it, and when we are DKIM signing, the signature header is appended to MIMEHeader
, so it does need the trailing break here.
$signHeader = preg_replace('/\r\n[ \t]+/', ' ', $signHeader); | ||
$signHeader = str_replace(["\r\n","\r"], ["\n","\n"], $signHeader); | ||
$signHeader = preg_replace('/\n[ \t]+/', ' ', $signHeader); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think these changes are really appropriate. As the comment on this function says:
Canonicalized headers should always use CRLF, regardless of mailer setting
PHPMailer tries to handle everything as CRLF
until it is not required to do so (e.g. to use mail()
). Inconsistent internal line break handling is something that was a major problem in 5.2 and was addressed in 6. So while this might fix your current issue (though I think that's strange because CRLF is required to calculate a DKIM signature correctly, even if the message is sent using mail()
), I think it's likely to cause problems in other situations. A receiver will always recalculate the signature using CRLF, so not doing the same when sending is asking for trouble.
I can confirm that this also fails in 6.1.4. Same message sent through SMTP is ok, sent using mail on Linux the DKIM signature is incorrect. |
When you use PHP mail() on linux then static::$LE will be changed from CRLF to LF to accomodate the native line ending represented by PHP_EOL. Where mail expects LF, the MTA behind it will convert all LF to CRLF. On Windows this is different since the native line ending is already CRLF and PHP mail() expects CRLFs. If you use SMTP then it always has to be CRLF. Suggested and tested fix - insert as first code lines in DKIM_HeaderC(): |
I approve. It has the same result as my fix, but is simpler to understand. Regarding line 1557, I still believe this concatenated static::$LE can generate problem by breaking the MIME header. At least it can be replaced by a "\r\n" as the last character of $signHeader in DKIM_HeaderC(). |
There is a built-in function for normalizing line breaks, so I used that instead, plus I added a couple of constants to make it clear when we are using |
"Please give the DKIM-tweaks branch a try." |
Ah, I was already doing an equivalent of what you proposed (that's what |
Yes, now I get a pass. |
It still does not pass the test when checking with PHPMailer/DKIMValidator:
But works when:
|
This means that the validation of the body part fails. Strange, this code version is working ok for me. |
The problem is not related to the body content. This extra static:$LE breaks the header in two parts. When I checked the body content (still using DKIMValidator) I found "To:" and "Subject:" as part of it, and of course they are not present in the MIME Header part. See here the extra break (the content of the body is just "test"):
Please make your own test with DKIMValidator. |
The header reordering is a thing that The bug here looks like the extra line break is appearing between |
I found where that happens, as you can see in the last commit. I think it's better to leave the other line break where it is, so it's clear when breaks are added during structural operations. Please give this a try with both |
I did the test with both SMTP and mail() and both look ok and validate DKIM. mailPassThru() call - The to and subject headers have been removed from the mime headers before. They are now specified separately on the mail() call: In the resulting email sent by mail() the To:, Subject: and X-PHP-Originating-Script: headers come first, followed by the merged mime headers. After that followed by X-... headers inserted by the mail server. I see no mixup of headers to body or incorrect line ends. For reference, the mail() test was on Linux with PHP 7.2. I did the SMTP test om Windows with PHP 7.3. |
I did the test with local mail() on macOS / PHP 7.4.2 / Composer (using DKIM-tweaks branch) and PHPMailer\DKIMValidator returned:
Also checked with smtp(), using both PHPMailer\DKIMValidator and https://dkimvalidator.com: it is successfull. |
Thanks for opening this PR; I have now merged the other branch that fixes the same things as this. |
DKIM signature does not work when static::$LE=="\n"
It appends when $this->Mailer!='smtp' AND PHP_OS!="WIN".
Here are the messages I got, using PHPMailer/DKIMValidator:
It works fine after the fix, using "mail" (verified with PHPMailer/DKIMValidator and some online DKIM services)