Permalink
Browse files

Add flag for using copiedHeaderFields and config for using extra head…

…ers in DKIM signature (#1468)

* Changed in DKIM signature

- Add flag for using copiedHeaderFields
- config for using extra headers in DKIM signature

* Cleanups after Review

- Don't use masked ampersands in url
- Remove test related artifacts after run

* Complete docs
  • Loading branch information...
gwi-mmuths authored and Synchro committed Jun 26, 2018
1 parent 46869d4 commit 5c9d3c548497390822369516691139f0a724b621
Showing with 153 additions and 13 deletions.
  1. +4 −0 examples/DKIM_sign.phps
  2. +59 −13 src/PHPMailer.php
  3. +90 −0 test/PHPMailerTest.php
View
@@ -32,6 +32,10 @@ $mail->DKIM_selector = 'phpmailer';
$mail->DKIM_passphrase = '';
//The identity you're signing as - usually your From address
$mail->DKIM_identity = $mail->From;
//Suppress listing signed header fields in signature, defaults to true for debugging purpose
$this->mailer->DKIM_copyHeaderFields = false;
//Optionally you can add extra headers for signing to meet special requirements
$this->mailer->DKIM_extraHeaders = ['List-Unsubscribe', 'List-Help'];
//When you send, the DKIM settings will be used to sign the message
if (!$mail->send()) {
View
@@ -457,6 +457,22 @@ class PHPMailer
*/
public $DKIM_domain = '';
/**
* DKIM Copy header field values for diagnostic use.
*
* @var bool
*/
public $DKIM_copyHeaderFields = true;
/**
* DKIM Extra signing headers.
*
* @example ['List-Unsubscribe', 'List-Help']
*
* @var array
*/
public $DKIM_extraHeaders = [];
/**
* DKIM private key file path.
*
@@ -4266,6 +4282,11 @@ public function DKIM_Add($headers_line, $subject, $body)
$to_header = '';
$date_header = '';
$current = '';
$copiedHeaderFields = '';
$foundExtraHeaders = [];
$extraHeaderKeys = '';
$extraHeaderValues = '';
$extraCopyHeaderFields = '';
foreach ($headers as $header) {
if (strpos($header, 'From:') === 0) {
$from_header = $header;
@@ -4276,6 +4297,23 @@ public function DKIM_Add($headers_line, $subject, $body)
} 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;
}
}
$foundExtraHeaders[$extraHeader] = $headerValue;
$current = '';
break;
}
}
} else {
if (!empty($$current) and strpos($header, ' =?') === 0) {
$$current .= $header;
@@ -4284,14 +4322,24 @@ public function DKIM_Add($headers_line, $subject, $body)
}
}
}
$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)
); // Copied header fields (dkim-quoted-printable)
foreach ($foundExtraHeaders as $key => $value) {
$extraHeaderKeys .= ':' . $key;
$extraHeaderValues .= $value . "\r\n";
if ($this->DKIM_copyHeaderFields) {
$extraCopyHeaderFields .= "\t|" . str_replace('|', '=7C', $this->DKIM_QP($value)) . ";\r\n";
}
}
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 = "\tz=$from\r\n" .
"\t|$to\r\n" .
"\t|$date\r\n" .
"\t|$subject;\r\n" .
$extraCopyHeaderFields;
}
$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
@@ -4307,19 +4355,17 @@ public function DKIM_Add($headers_line, $subject, $body)
$this->DKIM_selector .
";\r\n" .
"\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
"\th=From:To:Date:Subject;\r\n" .
"\th=From:To:Date:Subject" . $extraHeaderKeys . ";\r\n" .
"\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
"\tz=$from\r\n" .
"\t|$to\r\n" .
"\t|$date\r\n" .
"\t|$subject;\r\n" .
$copiedHeaderFields .
"\tbh=" . $DKIMb64 . ";\r\n" .
"\tb=";
$toSign = $this->DKIM_HeaderC(
$from_header . "\r\n" .
$to_header . "\r\n" .
$date_header . "\r\n" .
$subject_header . "\r\n" .
$extraHeaderValues .
$dkimhdrs
);
$signed = $this->DKIM_Sign($toSign);
View
@@ -1910,6 +1910,96 @@ public function testDKIMHeaderCanonicalization()
);
}
/**
* DKIM copied header fields tests.
*
* @group dkim
*
* @see https://tools.ietf.org/html/rfc6376#section-3.5
*/
public function testDKIMOptionalHeaderFieldsCopy()
{
$privatekeyfile = 'dkim_private.pem';
$pk = openssl_pkey_new(
[
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
]
);
openssl_pkey_export_to_file($pk, $privatekeyfile);
$this->Mail->DKIM_private = 'dkim_private.pem';
//Example from https://tools.ietf.org/html/rfc6376#section-3.5
$from = 'from@example.com';
$to = 'to@example.com';
$date = 'date';
$subject = 'example';
$headerLines = "From:$from\r\nTo:$to\r\nDate:$date\r\n";
$copyHeaderFields = "\tz=From:$from\r\n\t|To:$to\r\n\t|Date:$date\r\n\t|Subject:=20$subject;\r\n";
$this->Mail->DKIM_copyHeaderFields = true;
$this->assertContains(
$copyHeaderFields,
$this->Mail->DKIM_Add($headerLines, $subject, ''),
'DKIM header with copied header fields incorrect'
);
$this->Mail->DKIM_copyHeaderFields = false;
$this->assertNotContains(
$copyHeaderFields,
$this->Mail->DKIM_Add($headerLines, $subject, ''),
'DKIM header without copied header fields incorrect'
);
unlink($privatekeyfile);
}
/**
* DKIM signing extra headers tests.
*
* @group dkim
*/
public function testDKIMExtraHeaders()
{
$privatekeyfile = 'dkim_private.pem';
$pk = openssl_pkey_new(
[
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
]
);
openssl_pkey_export_to_file($pk, $privatekeyfile);
$this->Mail->DKIM_private = 'dkim_private.pem';
//Example from https://tools.ietf.org/html/rfc6376#section-3.5
$from = 'from@example.com';
$to = 'to@example.com';
$date = 'date';
$subject = 'example';
$anyHeader = 'foo';
$unsubscribeUrl = '<https://www.example.com/unsubscribe/?newsletterId=anytoken&amp;actionToken=anyToken' .
'&otherParam=otherValue&anotherParam=anotherVeryVeryVeryLongValue>';
$this->Mail->addCustomHeader('X-AnyHeader', $anyHeader);
$this->Mail->addCustomHeader('Baz', 'bar');
$this->Mail->addCustomHeader('List-Unsubscribe', $unsubscribeUrl);
$this->Mail->DKIM_extraHeaders = ['Baz', 'List-Unsubscribe'];
$headerLines = "From:$from\r\nTo:$to\r\nDate:$date\r\n";
$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';
$result = $this->Mail->DKIM_Add($headerLines, $subject, '');
$this->assertContains($headerFields, $result, 'DKIM header with extra headers incorrect');
unlink($privatekeyfile);
}
/**
* DKIM Signing tests.
*

0 comments on commit 5c9d3c5

Please sign in to comment.