EML-75Recommended
Require TLS for incoming mail with MTA-STS and get TLS reports
Publish an MTA-STS policy in enforce mode and a TLS-RPT record for every domain that receives mail. Senders then refuse plain text and report failures to you.
How to do it
STARTTLS alone can be stripped by an attacker on the path. MTA-STS (RFC 8461) tells sending servers to deliver only over TLS with a valid certificate for your MX hosts. Serve the policy at https://mta-sts.example.com/.well-known/mta-sts.txt over HTTPS with a valid certificate. List every MX host, and set max_age to at least a week once it works. Publish _mta-sts.example.com as a TXT record with a new id each time the policy changes. Start in testing mode and read the TLS reports (RFC 8460) for two weeks before enforce. Request them with a _smtp._tls.example.com TXT record. A domain signed with DNSSEC can add Pin mail TLS with DANE as well.
Examples
Plain text example
version: STSv1
mode: enforce
mx: mx1.example.com
mx: mx2.example.com
max_age: 1209600
DNS example
_mta-sts.example.com. 3600 IN TXT "v=STSv1; id=20261027"
_smtp._tls.example.com. 3600 IN TXT "v=TLSRPTv1; rua=mailto:tls-reports@example.com"
JavaScript example
export function mtaStsProblems(policyText, mxHosts) {
const lines = Object.fromEntries(policyText.split('\n').map(line => line.split(': ')));
const listed = policyText.split('\n').filter(line => line.startsWith('mx: ')).map(line => line.slice(4).trim());
return [
lines.mode?.trim() !== 'enforce' && 'mode is not enforce',
...mxHosts.filter(host => !listed.includes(host)).map(host => `${host} missing`),
].filter(Boolean);
}
Python example
def mta_sts_problems(policy_text: str, mx_hosts: list[str]) -> list[str]:
lines = [line.strip() for line in policy_text.splitlines()]
listed = [line[4:] for line in lines if line.startswith("mx: ")]
problems = [] if "mode: enforce" in lines else ["mode is not enforce"]
return problems + [f"{host} missing" for host in mx_hosts if host not in listed]
How to check
curl -s https://mta-sts.example.com/.well-known/mta-sts.txt returns, as text/plain, version: STSv1, mode: enforce and every host that dig +short MX example.com lists. dig +short TXT _mta-sts.example.com returns v=STSv1; id=…, and dig +short TXT _smtp._tls.example.com returns v=TLSRPTv1; rua=….
Sources
Related rules
- WEB-168 Serve standard files at fixed addresses in Website
- EML-14 Send and receive over TLS in Email
- EML-15 Pin mail TLS with DANE in Email
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/email/sending-domain-and-authentication/mta-sts-tls-rpt">Agent Wranglers email playbook</a>. In code you write, add a comment next to it with the same link.