[wp-trac] [WordPress Trac] #62940: wp_mail(): Address header parsing is not RFC-5322 complient and fails on quoted-string when including a "<", ">" or ", "

WordPress Trac noreply at wordpress.org
Sun Jul 13 17:58:07 UTC 2025


#62940: wp_mail(): Address header parsing is not RFC-5322 complient and fails on
quoted-string when including a "<", ">" or ","
-------------------------------------------------+-------------------------
 Reporter:  bhujagendra                          |       Owner:  (none)
     Type:  enhancement                          |      Status:  new
 Priority:  normal                               |   Milestone:  Awaiting
                                                 |  Review
Component:  Mail                                 |     Version:  2.1.1
 Severity:  normal                               |  Resolution:
 Keywords:  needs-patch needs-unit-tests has-    |     Focuses:
  test-info                                      |
-------------------------------------------------+-------------------------
Changes (by SirLouen):

 * keywords:   => needs-patch needs-unit-tests has-test-info
 * version:  trunk => 2.1.1
 * type:  defect (bug) => enhancement


Comment:

 We have to divide this into 3 bugs or enhancements (I would have created 3
 tickets, one for each one), and probably we could find an answer for each
 one individually instead of treating them as a single problem (although,
 by the end I think that there could be a single solution that could
 benefit all 3 scenarios).

 Also, these would have avoided this macro questions and answers that are
 ultra-tiring for most reviewers that will prefer to skip for the future in
 most cases.

 These problems have been here pretty much since the integration of
 PHPMailer in [4946]

 == Firstly, about parsing the various forms like `From: "test<stage"
 <test at example.com>`

 Replying to [ticket:62940 bhujagendra]:
 > The [https://www.php.net/manual/en/function.mail.php php mail()
 documentation] does not explicitly state that the `From` header needs to
 comply with RFC 2822. But it does say so for the `To` header. Furthermore,
 the current implementation does already support the `name-
 addr`-[https://datatracker.ietf.org/doc/html/rfc2822#section-3.4 syntax]
 (i.e. `[display-name] angle-addr`), which was not allowed in the preceding
 [https://datatracker.ietf.org/doc/html/rfc822#section-4.4.1 RFC 822].

 `mail` doesn't specify anything about the `from` because it doesn't have
 any specific limitations regarding the `headers` which are left to the
 final decision of the MTA (and you can pretty set whatever custom header
 you like).

 This doesn't mean that any development should or should not take care of
 this. In the case of `PHPMailer` it provides a way of taking care of the
 `From:` part from the headers.

 Remember that in `wp_mail` the `From:` parsing from Headers
 [https://github.com/WordPress/wordpress-
 develop/blob/d71f29ff0899c6eb29bc037a7c521a966405cb35/src/wp-
 includes/pluggable.php#L301-L319 you mention here] was declared legacy
 [5639] and ideally not to be manipulated from there, but from the hook
 `wp_mail_from` (similar fashion as using any other raw thing, instead of
 the provided functionality, which is causing some troubles in other areas)

 So basically, using `Headers` is a bad idea, and still PHPMailer will fail
 with such format if you directly use the hook:
 `PHPMailer Error: Invalid address:  (From): "test<stage"
 <test at example.com>`

 So technically, this should be a thing that
 [https://github.com/PHPMailer/PHPMailer/issues should be discussed
 upstream]. Plus, with just one From, without a Sender header, you cannot
 really bypass the `addFrom` method from `PHPMailer` if you want to
 actually send the email and it to be accepted by any MTA (with many
 additional problems I'm explaining after).

 == Second, the multiple `From:` issue:

 This is a little incremental to the first point, because now, we can
 actually introduce a Sender header, so we could play around `From:` a
 little more.

 Let's say we go into the `wp_mail_from` and we directly pass:

 {{{
 add_filter('wp_mail_from', function($original_email_address) {
      return 'test <test at example.com>, User <user at example.com>';
 });
 }}}

 What `PHPMailer` will reject is that `From` format. It might ultimately
 reject any of the forms you are thinking for the `From:`.

 Moreover, this is one of the least recommended practices,
 [https://serverfault.com/questions/841465/rejected-emails-messages-with-
 multiple-addresses-in-from-header-are-not-accept as many servers] will
 reject this kind of header.

 Although it's true that the specification permits this according to
 [https://datatracker.ietf.org/doc/html/rfc5322#section-3.6.2 RFC 5322], we
 need to add the Sender field in the header.

 So yes, we could add everything in the Custom Headers, but the idea moves
 towards handling everything without a full custom email in the `PHPMailer`
 fashion. Or you could do something like this:

 {{{
 add_action('phpmailer_init', function ($phpmailer) {
         $phpmailer->addCustomHeader( 'From', 'User <user at example.com>' );
         $phpmailer->addCustomHeader( 'Sender', 'test <sender at example.com>'
 );
 });

 add_filter('wp_mail_from', function() {
         return 'test at example.com';
 });

 add_filter('wp_mail_from_name', function() {
         return 'test';
 });
 }}}

 To achieve the same behaviour (although, if you set a `From:` in the
 format like in the first part, it will fail with the same PHPMailer error)

 == Third, about the `To:`, `CC:`… parsing

 Here, as in the first, I agree that the parsing is completely
 insufficient.

 But in this case, we don't really have an easy way to work around it. So I
 think is the main bug that should be sorted out. Although I have to agree
 with you that despite the Header part is legacy, sorting it out should
 also be done (and remove it from "legacy" because playing around with the
 Headers should be completely functional).

 Personally, I would approach this with a specific sanitization function,
 extracting it from `wp_mail`, and using such to parse each of the
 potential areas where an email address could be present compliant with RFC
 2822

 So I invite you to work on a possible fix (also unit tests will be 100%
 needed for this fix)

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/62940#comment:1>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list