jakartaee / mail-api

Jakarta Mail Specification project
https://jakartaee.github.io/mail-api
Other
244 stars 101 forks source link

Message-ID #728

Open patch-work opened 5 months ago

patch-work commented 5 months ago

Hello,

JavaMail sends non-conformant Message-IDs that are catched by spam filters.

Be a good netizen and conform to the standard.

Thank you.

RFC-822 [pg. 30]

A domain-ref must be THE official name of a registry, network, or host. It is a symbolic reference, within a name subdomain.

RFC-822 [pg. 23] 4.6.1. MESSAGE-ID / RESENT-MESSAGE-ID

This field contains a unique identifier (the local-part address unit) which refers to THIS version of THIS message. The uniqueness of the message identifier is guaranteed by the host which generates it. This identifier is intended to be machine readable and not necessarily meaningful to humans. A message identifier pertains to exactly one instantiation of a particular message; subsequent revisions to the message should each receive new message identifiers.

RFC-822 [pp. 44-46]

addr-spec = local-part "@" domain ; global address [...] domain = sub-domain *("." sub-domain) [...] msg-id = "<" addr-spec ">" ; Unique message id optional-field = / "Message-ID" ":" msg-id[...] sub-domain = domain-ref / domain-literal

jmehrens commented 5 months ago
  1. What version of JavaMail or JakartaMail?
  2. What spam filter?
  3. What text is shown as to why it fails the spam filter rule?
  4. What are your mail session properties? You can redact the values just knowing the keys helps.

Let's create a test program to generate the Message-ID using JakartaMail 2.1.3:

public static void main(String[] args) throws Exception {
        //InetAddress.getCanonicalHostName if system property 
        //mail.mime.address.usecanonicalhostname is not forced to false
        //setting the mail.<protocol>.host does not change local address
        Properties props = new Properties();
        props.put("mail.smtp.host", "some.smtp.host");
        props.put("mail.smtps.host", "some.smtps.host");
        props.put("mail.imap.host", "some.imap.host");
        props.put("mail.imaps.host", "some.imaps.host");
        writeMessage(props);

        //mail.host controls the domain
        props.put("mail.host", "some.mail.host");
        writeMessage(props);

        //mail.from controls the domain over mail.host
        props.put("mail.from", "some@from.host");
        writeMessage(props);
    }

    private static void writeMessage(Properties props) throws Exception {
        MimeMessage m = new MimeMessage(Session.getInstance(props));
        m.setText("");
        m.saveChanges();
        m.writeTo(System.out);
        System.out.flush();
    }

On my machine this outputs:

Date: Sat, 13 Apr 2024 01:23:23 -0500 (CDT)
Message-ID: <429313384.0.1712989403934@devbox-VirtualBox>
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Date: Sat, 13 Apr 2024 01:23:23 -0500 (CDT)
Message-ID: <1151020327.1.1712989403960@some.mail.host>
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Date: Sat, 13 Apr 2024 01:23:23 -0500 (CDT)
Message-ID: <736709391.2.1712989403976@from.host>
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

RFC-822 [pg. 39]

A.3.2 Using some of the additional fields [snip] Message-ID: some.string@SHOST

A.3.3 About as complex as you're going to get [snip] Message-ID: 4231.629.XYzi-What@Other-Host

From what I can tell JakartaMail matches the Appendix A.3.3 of RFC-822. If the spam rule is failing due to the domain not matching then that is a configuration problem on your sending machine where you need to correct the canonical hostname lookup or simply specify mail.host or mail.from.

More information is needed.

jmehrens commented 5 months ago

Related https://github.com/jakartaee/mail-api/issues/460

patch-work commented 5 months ago

This project changed ownership and name a few times, and old web pages are not in sync. As a result, users of the project are not necessarily using the latest version.

When I read JavaMail in the mid, then they are using an old version. Hard to say which one.

I see from your demo above that the new mid does not include a reference to the project. This makes it hard for us to help offending senders. If the name of the project is in the mid, then we know they are using a CMS of some sort, so the blame for misconfiguration is on the software house writing the CMS. If you hide the "JavaMail" or "JakartaMail" reference, it will be impossible to offer a reference like this.

Still on the new mid, it is still possible for a user to specify a non-conformant domain-ref. The software should include a runtime sanity check, and fail hard when the domain-ref is not a valid FQDN. Ideally, to solve the problem once and for all, the user should not specify any such parameter: the software ought to figure-out the public FQDN of the server and propose it to the user as sane default during initial configuration. Then the user can accept or improve the default. In this case, the configuration should mention the RFC, and include a runtime test to validate the user's value. Again, the software should fail hard in case of non compliance.

I would also put a big red notice on all old web pages of the project, pointing at the new page here.

jmehrens commented 5 months ago

I see from your demo above that the new mid does not include a reference to the project. This makes it hard for us to help offending senders.

That is a ticket for the software using JakartaMail. They should be setting a User-Agent and or X-Mailer header on the MimeMessage to identify itself.

If you hide the "JavaMail" or "JakartaMail" reference, it will be impossible to offer a reference like this.

This is intentional to hide this information for security reasons: Don't include user name in Message-ID. There still is one path that will write JakartaMail if things have gone wrong. This should be scrubbed too.

Still on the new mid, it is still possible for a user to specify a non-conformant domain-ref. The software should include a runtime sanity check, and fail hard when the domain-ref is not a valid FQDN.

There are improvements here but they are limited to syntax checking which seems reasonable. Name lookup will be avoided due to cost of the lookup which is a known problem.

Ideally, to solve the problem once and for all, the user should not specify any such parameter: the software ought to figure-out the public FQDN of the server and propose it to the user as sane default during initial configuration. Then the user can accept or improve the default.

That is a issue ticket for the software using JakartaMail. InternetAddress::getLocalAddress is what is used to compute the Message-ID. That method returns an object with a validation method. Consumers of JakartaMail can use that method to validate the Message-ID domain.

I'll look at making some of these changes.