mirror of
https://github.com/PHPMailer/PHPMailer.git
synced 2024-09-20 10:02:14 +02:00
Merge branch 'dkimrevision'
This commit is contained in:
commit
11e8d23927
@ -25,10 +25,10 @@
|
||||
$domain = 'example.com';
|
||||
$selector = 'phpmailer';
|
||||
|
||||
//Path to your private key:
|
||||
$privatekeyfile = 'dkim_private.pem';
|
||||
//Path to your public key:
|
||||
$publickeyfile = 'dkim_public.pem';
|
||||
//Private key filename for this selector
|
||||
$privatekeyfile = $selector . '_dkim_private.pem';
|
||||
//Public key filename for this selector
|
||||
$publickeyfile = $selector . '_dkim_public.pem';
|
||||
|
||||
if (file_exists($privatekeyfile)) {
|
||||
echo "Using existing keys - if you want to generate new keys, delete old key files first.\n\n";
|
||||
@ -57,8 +57,9 @@ echo "\n\nPublic key:\n\n" . $publickey;
|
||||
//Prep public key for DNS, e.g.
|
||||
//phpmailer._domainkey.example.com IN TXT "v=DKIM1; h=sha256; t=s; p=" "MIIBIjANBg...oXlwIDAQAB"...
|
||||
$dnskey = "$selector._domainkey.$domain IN TXT";
|
||||
$dnsvalue = '"v=DKIM1; h=sha256; t=s; p=" ';
|
||||
//Some DNS server don't like ; chars unless backslash-escaped
|
||||
$dnsvalue = '"v=DKIM1\; h=sha256\; t=s\; p=" ';
|
||||
$dnsvalue2 = '"v=DKIM1\; h=sha256\; t=s\; p=" ';
|
||||
|
||||
//Strip and split the key into smaller parts and format for DNS
|
||||
//Many DNS systems don't like long TXT entries
|
||||
@ -72,6 +73,8 @@ $keyparts = str_split($publickey, 253); //Becomes 255 when quotes are included
|
||||
//Quote each chunk
|
||||
foreach ($keyparts as $keypart) {
|
||||
$dnsvalue .= '"' . trim($keypart) . '" ';
|
||||
$dnsvalue2 .= '"' . trim($keypart) . '" ';
|
||||
}
|
||||
echo "\n\nDNS key:\n\n" . trim($dnskey);
|
||||
echo "\n\nDNS value:\n\n" . trim($dnsvalue);
|
||||
echo "\n\nDNS value (with escaping):\n\n" . trim($dnsvalue2);
|
||||
|
@ -2487,7 +2487,7 @@ class PHPMailer
|
||||
}
|
||||
|
||||
if ('mail' !== $this->Mailer) {
|
||||
$result .= static::$LE;
|
||||
// $result .= static::$LE;
|
||||
}
|
||||
|
||||
return $result;
|
||||
@ -4468,7 +4468,7 @@ class PHPMailer
|
||||
//@see https://tools.ietf.org/html/rfc5322#section-2.2
|
||||
//That means this may break if you do something daft like put vertical tabs in your headers.
|
||||
//Unfold header lines
|
||||
$signHeader = preg_replace('/\r\n[ \t]+/m', '', $signHeader);
|
||||
$signHeader = preg_replace('/\r\n[ \t]+/', ' ', $signHeader);
|
||||
//Break headers out into an array
|
||||
$lines = explode("\r\n", $signHeader);
|
||||
foreach ($lines as $key => $line) {
|
||||
@ -4530,102 +4530,137 @@ class PHPMailer
|
||||
public function DKIM_Add($headers_line, $subject, $body)
|
||||
{
|
||||
$DKIMsignatureType = 'rsa-sha256'; // Signature & hash algorithms
|
||||
$DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
|
||||
$DKIMcanonicalization = 'relaxed/simple'; // Canonicalization methods of header & body
|
||||
$DKIMquery = 'dns/txt'; // Query method
|
||||
$DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
|
||||
$subject_header = "Subject: $subject";
|
||||
$headers = explode(static::$LE, $headers_line);
|
||||
$from_header = '';
|
||||
$to_header = '';
|
||||
$date_header = '';
|
||||
$current = '';
|
||||
$copiedHeaderFields = '';
|
||||
$foundExtraHeaders = [];
|
||||
$extraHeaderKeys = '';
|
||||
$extraHeaderValues = '';
|
||||
$extraCopyHeaderFields = '';
|
||||
foreach ($headers as $header) {
|
||||
if (strpos($header, 'From:') === 0) {
|
||||
$from_header = $header;
|
||||
$current = 'from_header';
|
||||
} elseif (strpos($header, 'To:') === 0) {
|
||||
$to_header = $header;
|
||||
$current = 'to_header';
|
||||
} elseif (strpos($header, 'Date:') === 0) {
|
||||
$date_header = $header;
|
||||
$current = 'date_header';
|
||||
} elseif (!empty($this->DKIM_extraHeaders)) {
|
||||
foreach ($this->DKIM_extraHeaders as $extraHeader) {
|
||||
if (strpos($header, $extraHeader . ':') === 0) {
|
||||
$headerValue = $header;
|
||||
foreach ($this->CustomHeader as $customHeader) {
|
||||
if ($customHeader[0] === $extraHeader) {
|
||||
$headerValue = trim($customHeader[0]) .
|
||||
': ' .
|
||||
$this->encodeHeader(trim($customHeader[1]));
|
||||
break;
|
||||
}
|
||||
$DKIMtime = time();
|
||||
//Always sign these headers without being asked
|
||||
$autoSignHeaders = [
|
||||
'From',
|
||||
'To',
|
||||
'CC',
|
||||
'Date',
|
||||
'Subject',
|
||||
'Reply-To',
|
||||
'Message-ID',
|
||||
'Content-Type',
|
||||
'Mime-Version',
|
||||
'X-Mailer',
|
||||
];
|
||||
if (stripos($headers_line, 'Subject') === false) {
|
||||
$headers_line .= 'Subject: ' . $subject . static::$LE;
|
||||
}
|
||||
$headerLines = explode(static::$LE, $headers_line);
|
||||
$currentHeaderLabel = '';
|
||||
$currentHeaderValue = '';
|
||||
$parsedHeaders = [];
|
||||
$headerLineIndex = 0;
|
||||
$headerLineCount = count($headerLines);
|
||||
foreach ($headerLines as $headerLine) {
|
||||
$matches = [];
|
||||
if (preg_match('/^([^ \t]*?)(?::[ \t]*)(.*)$/', $headerLine, $matches)) {
|
||||
if ($currentHeaderLabel !== '') {
|
||||
//We were previously in another header; This is the start of a new header, so save the previous one
|
||||
$parsedHeaders[] = ['label' => $currentHeaderLabel, 'value' => $currentHeaderValue];
|
||||
}
|
||||
$currentHeaderLabel = $matches[1];
|
||||
$currentHeaderValue = $matches[2];
|
||||
} elseif (preg_match('/^[ \t]+(.*)$/', $headerLine, $matches)) {
|
||||
//This is a folded continuation of the current header, so unfold it
|
||||
$currentHeaderValue .= ' ' . $matches[1];
|
||||
}
|
||||
++$headerLineIndex;
|
||||
if ($headerLineIndex >= $headerLineCount) {
|
||||
//This was the last line, so finish off this header
|
||||
$parsedHeaders[] = ['label' => $currentHeaderLabel, 'value' => $currentHeaderValue];
|
||||
}
|
||||
}
|
||||
$copiedHeaders = [];
|
||||
$headersToSignKeys = [];
|
||||
$headersToSign = [];
|
||||
foreach ($parsedHeaders as $header) {
|
||||
//Is this header one that must be included in the DKIM signature?
|
||||
if (in_array($header['label'], $autoSignHeaders, true)) {
|
||||
$headersToSignKeys[] = $header['label'];
|
||||
$headersToSign[] = $header['label'] . ': ' . $header['value'];
|
||||
if ($this->DKIM_copyHeaderFields) {
|
||||
$copiedHeaders[] = $header['label'] . ':' . //Note no space after this, as per RFC
|
||||
str_replace('|', '=7C', $this->DKIM_QP($header['value']));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
//Is this an extra custom header we've been asked to sign?
|
||||
if (in_array($header['label'], $this->DKIM_extraHeaders, true)) {
|
||||
//Find its value in custom headers
|
||||
foreach ($this->CustomHeader as $customHeader) {
|
||||
if ($customHeader[0] === $header['label']) {
|
||||
$headersToSignKeys[] = $header['label'];
|
||||
$headersToSign[] = $header['label'] . ': ' . $header['value'];
|
||||
if ($this->DKIM_copyHeaderFields) {
|
||||
$copiedHeaders[] = $header['label'] . ':' . //Note no space after this, as per RFC
|
||||
str_replace('|', '=7C', $this->DKIM_QP($header['value']));
|
||||
}
|
||||
$foundExtraHeaders[$extraHeader] = $headerValue;
|
||||
$current = '';
|
||||
break;
|
||||
//Skip straight to the next header
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
} elseif (!empty($$current) && strpos($header, ' =?') === 0) {
|
||||
$$current .= $header;
|
||||
} else {
|
||||
$current = '';
|
||||
}
|
||||
}
|
||||
foreach ($foundExtraHeaders as $key => $value) {
|
||||
$extraHeaderKeys .= ':' . $key;
|
||||
$extraHeaderValues .= $value . "\r\n";
|
||||
if ($this->DKIM_copyHeaderFields) {
|
||||
$extraCopyHeaderFields .= ' |' . str_replace('|', '=7C', $this->DKIM_QP($value)) . ";\r\n";
|
||||
$copiedHeaderFields = '';
|
||||
if ($this->DKIM_copyHeaderFields && count($copiedHeaders) > 0) {
|
||||
//Assemble a DKIM 'z' tag
|
||||
$copiedHeaderFields = ' z=';
|
||||
$first = true;
|
||||
foreach ($copiedHeaders as $copiedHeader) {
|
||||
if (!$first) {
|
||||
$copiedHeaderFields .= static::$LE . ' |';
|
||||
}
|
||||
//Fold long values
|
||||
if (strlen($copiedHeader) > self::STD_LINE_LENGTH - 3) {
|
||||
$copiedHeaderFields .= substr(
|
||||
chunk_split($copiedHeader, self::STD_LINE_LENGTH - 3, static::$LE . ' '),
|
||||
0,
|
||||
-strlen(static::$LE . ' ')
|
||||
);
|
||||
} else {
|
||||
$copiedHeaderFields .= $copiedHeader;
|
||||
}
|
||||
$first = false;
|
||||
}
|
||||
$copiedHeaderFields .= ';' . static::$LE;
|
||||
}
|
||||
if ($this->DKIM_copyHeaderFields) {
|
||||
$from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
|
||||
$to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
|
||||
$date = str_replace('|', '=7C', $this->DKIM_QP($date_header));
|
||||
$subject = str_replace('|', '=7C', $this->DKIM_QP($subject_header));
|
||||
$copiedHeaderFields = " z=$from\r\n" .
|
||||
" |$to\r\n" .
|
||||
" |$date\r\n" .
|
||||
" |$subject;\r\n" .
|
||||
$extraCopyHeaderFields;
|
||||
}
|
||||
$headerKeys = ' h=' . implode(':', $headersToSignKeys) . ';' . static::$LE;
|
||||
$headerValues = implode(static::$LE, $headersToSign);
|
||||
$body = $this->DKIM_BodyC($body);
|
||||
$DKIMlen = strlen($body); // Length of body
|
||||
$DKIMb64 = base64_encode(pack('H*', hash('sha256', $body))); // Base64 of packed binary SHA-256 hash of body
|
||||
if ('' === $this->DKIM_identity) {
|
||||
$ident = '';
|
||||
} else {
|
||||
$ident = ' i=' . $this->DKIM_identity . ';';
|
||||
$ident = '';
|
||||
if ('' !== $this->DKIM_identity) {
|
||||
$ident = ' i=' . $this->DKIM_identity . ';' . static::$LE;
|
||||
}
|
||||
$dkimhdrs = 'DKIM-Signature: v=1; a=' .
|
||||
$DKIMsignatureType . '; q=' .
|
||||
$DKIMquery . '; l=' .
|
||||
$DKIMlen . '; s=' .
|
||||
$this->DKIM_selector .
|
||||
";\r\n" .
|
||||
' t=' . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
|
||||
' h=From:To:Date:Subject' . $extraHeaderKeys . ";\r\n" .
|
||||
' d=' . $this->DKIM_domain . ';' . $ident . "\r\n" .
|
||||
//The DKIM-Signature header is included in the signature *except for* the value of the `b` tag
|
||||
//which is appended after calculating the signature
|
||||
//https://tools.ietf.org/html/rfc6376#section-3.5
|
||||
$dkimSignatureHeader = 'DKIM-Signature: v=1;' .
|
||||
' d=' . $this->DKIM_domain . ';' .
|
||||
' s=' . $this->DKIM_selector . ';' . static::$LE .
|
||||
' a=' . $DKIMsignatureType . ';' .
|
||||
' q=' . $DKIMquery . ';' .
|
||||
' l=' . $DKIMlen . ';' .
|
||||
' t=' . $DKIMtime . ';' .
|
||||
' c=' . $DKIMcanonicalization . ';' . static::$LE .
|
||||
$headerKeys .
|
||||
$ident .
|
||||
$copiedHeaderFields .
|
||||
' bh=' . $DKIMb64 . ";\r\n" .
|
||||
' bh=' . $DKIMb64 . ';' . static::$LE .
|
||||
' b=';
|
||||
$toSign = $this->DKIM_HeaderC(
|
||||
$from_header . "\r\n" .
|
||||
$to_header . "\r\n" .
|
||||
$date_header . "\r\n" .
|
||||
$subject_header . "\r\n" .
|
||||
$extraHeaderValues .
|
||||
$dkimhdrs
|
||||
//Canonicalize the set of headers
|
||||
$canonicalizedHeaders = $this->DKIM_HeaderC(
|
||||
$headerValues . static::$LE . $dkimSignatureHeader
|
||||
);
|
||||
$signed = $this->DKIM_Sign($toSign);
|
||||
$signature = $this->DKIM_Sign($canonicalizedHeaders);
|
||||
$signature = trim(chunk_split($signature, self::STD_LINE_LENGTH - 3, static::$LE . ' '));
|
||||
|
||||
return static::normalizeBreaks($dkimhdrs . $signed) . static::$LE;
|
||||
return static::normalizeBreaks($dkimSignatureHeader . $signature) . static::$LE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2089,13 +2089,13 @@ EOT;
|
||||
$this->Mail->DKIM_HeaderC($preheaders),
|
||||
'DKIM header canonicalization incorrect'
|
||||
);
|
||||
//Check that long folded lines with runs of spaces are canonicalised properly
|
||||
//Check that long folded lines with runs of spaces are canonicalized properly
|
||||
$preheaders = 'Long-Header-1: <https://example.com/somescript.php?' .
|
||||
"id=1234567890&name=Abcdefghijklmnopquestuvwxyz&hash=\r\n abc1234" .
|
||||
"\r\nLong-Header-2: This is a long header value that contains runs of spaces and trailing " .
|
||||
"\r\n and is folded onto 2 lines";
|
||||
"id=1234567890&name=Abcdefghijklmnopquestuvwxyz&hash=\r\n abc1234\r\n" .
|
||||
"Long-Header-2: This is a long header value that contains runs of spaces and trailing \r\n" .
|
||||
' and is folded onto 2 lines';
|
||||
$postheaders = 'long-header-1:<https://example.com/somescript.php?id=1234567890&' .
|
||||
"name=Abcdefghijklmnopquestuvwxyz&hash=abc1234\r\nlong-header-2:This is a long" .
|
||||
"name=Abcdefghijklmnopquestuvwxyz&hash= abc1234\r\nlong-header-2:This is a long" .
|
||||
' header value that contains runs of spaces and trailing and is folded onto 2 lines';
|
||||
$this->assertEquals(
|
||||
$postheaders,
|
||||
@ -2130,7 +2130,7 @@ EOT;
|
||||
$subject = 'example';
|
||||
|
||||
$headerLines = "From:$from\r\nTo:$to\r\nDate:$date\r\n";
|
||||
$copyHeaderFields = " z=From:$from\r\n |To:$to\r\n |Date:$date\r\n |Subject:=20$subject;\r\n";
|
||||
$copyHeaderFields = " z=From:$from\r\n |To:$to\r\n |Date:$date\r\n |Subject:$subject;\r\n";
|
||||
|
||||
$this->Mail->DKIM_copyHeaderFields = true;
|
||||
$this->assertContains(
|
||||
@ -2185,7 +2185,7 @@ EOT;
|
||||
$headerLines .= "X-AnyHeader:$anyHeader\r\nBaz:bar\r\n";
|
||||
$headerLines .= 'List-Unsubscribe:' . $this->Mail->encodeHeader($unsubscribeUrl) . "\r\n";
|
||||
|
||||
$headerFields = 'h=From:To:Date:Subject:Baz:List-Unsubscribe';
|
||||
$headerFields = 'h=From:To:Date:Baz:List-Unsubscribe:Subject';
|
||||
|
||||
$result = $this->Mail->DKIM_Add($headerLines, $subject, '');
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user