RFC 8461

SMTP MTA Strict Transport Security (MTA-STS)

πŸ“… published September 2018, from ietf

SMTP MTA Strict Transport Security (MTA-STS) πŸ’–

Okay bestie, this is RFC 8461 πŸ’Œ It's a Standards Track doc from September 2018 (ISSN 2070-1721). The authors are D. Margolis and M. Risher (both Google, Inc.), B. Ramakrishnan (Oath, Inc.), A. Brotman (Comcast, Inc.), and J. Jones (Microsoft, Inc.). The header doesn't list anything it obsoletes or updates, so it's not replacing or changing any other RFC πŸŽ€

Part 1 of 5, so more is coming! 🧚

Abstract

MTA-STS is the SMTP MTA Strict Transport Security mechanism, and it lets mail service providers (SPs) say they can receive Transport Layer Security (TLS) secure SMTP connections. It also lets them say whether sending SMTP servers should refuse to deliver to MX hosts that don't offer TLS with a trusted server certificate πŸ”βœ¨

Status of This Memo

This is an Internet Standards Track document 🌸

It's a product of the Internet Engineering Task Force (IETF), so it represents the consensus of the IETF community. It got public review and was approved for publication by the Internet Engineering Steering Group (IESG). For more on Internet Standards, check out Section 2 of RFC 7841 πŸ¦‹

You can find info about the current status of this document, any errata, and how to give feedback at https://www.rfc-editor.org/info/rfc8461 πŸ’…

Copyright (c) 2018 IETF Trust and the persons identified as the document authors. All rights reserved 🩷

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) as they stood on the date this document was published. Please review those documents carefully, because they explain your rights and restrictions for this document πŸ“œ

Code Components pulled out of this document must include Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions. They come without warranty, as described in the Simplified BSD License πŸ’œ

Table of Contents

   1.  Introduction  . . . . . . . . . . . . . . . . . . . . . . . .   4
     1.1.  Terminology . . . . . . . . . . . . . . . . . . . . . . .   4
   2.  Related Technologies  . . . . . . . . . . . . . . . . . . . .   5
   3.  Policy Discovery  . . . . . . . . . . . . . . . . . . . . . .   5
     3.1.  MTA-STS TXT Records . . . . . . . . . . . . . . . . . . .   6
     3.2.  MTA-STS Policies  . . . . . . . . . . . . . . . . . . . .   7
     3.3.  HTTPS Policy Fetching . . . . . . . . . . . . . . . . . .  10
     3.4.  Policy Selection for Smart Hosts and Subdomains . . . . .  11
   4.  Policy Validation . . . . . . . . . . . . . . . . . . . . . .  11
     4.1.  MX Host Validation  . . . . . . . . . . . . . . . . . . .  12
     4.2.  Recipient MTA Certificate Validation  . . . . . . . . . .  12
   5.  Policy Application  . . . . . . . . . . . . . . . . . . . . .  12
     5.1.  Policy Application Control Flow . . . . . . . . . . . . .  13
   6.  Reporting Failures  . . . . . . . . . . . . . . . . . . . . .  13
   7.  Interoperability Considerations . . . . . . . . . . . . . . .  14
     7.1.  SNI Support . . . . . . . . . . . . . . . . . . . . . . .  14
     7.2.  Minimum TLS Version Support . . . . . . . . . . . . . . .  14
   8.  Operational Considerations  . . . . . . . . . . . . . . . . .  15
     8.1.  Policy Updates  . . . . . . . . . . . . . . . . . . . . .  15
     8.2.  Policy Delegation . . . . . . . . . . . . . . . . . . . .  15
     8.3.  Removing MTA-STS  . . . . . . . . . . . . . . . . . . . .  16
     8.4.  Preserving MX Candidate Traversal . . . . . . . . . . . .  17
   9.  IANA Considerations . . . . . . . . . . . . . . . . . . . . .  17
     9.1.  Well-Known URIs Registry  . . . . . . . . . . . . . . . .  17
     9.2.  MTA-STS TXT Record Fields . . . . . . . . . . . . . . . .  17
     9.3.  MTA-STS Policy Fields . . . . . . . . . . . . . . . . . .  18
   10. Security Considerations . . . . . . . . . . . . . . . . . . .  18
     10.1.  Obtaining a Signed Certificate . . . . . . . . . . . . .  18
     10.2.  Preventing Policy Discovery  . . . . . . . . . . . . . .  19
     10.3.  Denial of Service  . . . . . . . . . . . . . . . . . . .  19
     10.4.  Weak Policy Constraints  . . . . . . . . . . . . . . . .  20
     10.5.  Compromise of the Web PKI System . . . . . . . . . . . .  20
   11. References  . . . . . . . . . . . . . . . . . . . . . . . . .  21
     11.1.  Normative References . . . . . . . . . . . . . . . . . .  21
     11.2.  Informative References . . . . . . . . . . . . . . . . .  23
   Appendix A.  MTA-STS Example Record and Policy  . . . . . . . . .  25
   Appendix B.  Message Delivery Pseudocode  . . . . . . . . . . . .  25
   Contributors  . . . . . . . . . . . . . . . . . . . . . . . . . .  28
   Authors' Addresses  . . . . . . . . . . . . . . . . . . . . . . .  29

1. Introduction ✨

The STARTTLS extension to SMTP [RFC3207] lets SMTP clients and hosts negotiate a TLS channel for encrypted mail transmission πŸ“¨

Okay, so this opportunistic encryption protocol on its own gives a high barrier against passive man-in-the-middle interception πŸ›‘οΈ But any attacker who can delete parts of the SMTP session (like the "250 STARTTLS" response), or who can redirect the whole SMTP session (maybe by overwriting the resolved MX record of the delivery domain), can pull off downgrade or interception attacks πŸ˜­πŸ’€

This document defines a mechanism for recipient domains to publish policies, using a mix of DNS and HTTPS 🌈 Those policies specify:

  • whether MTAs sending mail to this domain can expect PKIX-authenticated TLS support
  • what a conforming client should do with messages when TLS cannot be successfully negotiated

1.1. Terminology πŸ’…

Here's the key bit: the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" are to be read as described in BCP 14 [RFC2119] [RFC8174], but only when they show up in ALL CAPS like this πŸ”‘

We also define these terms for use later in the doc πŸ“

  • MTA-STS Policy: A commitment by the Policy Domain to support TLS authenticated with PKIX [RFC5280] for the specified MX hosts.
  • Policy Domain: The domain that an MTA-STS Policy is defined for. This is the next-hop domain. When you send mail to "alice@example.com", this is usually "example.com", but explicit routing rules can override it (see Section 3.4, "Policy Selection for Smart Hosts and Subdomains") πŸ¦‹
  • Policy Host: The HTTPS host that serves the MTA-STS Policy for a Policy Domain. The rules for building the hostname are in Section 3.2, "MTA-STS Policies" 🌸
  • Sender or Sending MTA: The SMTP MTA that sends an email message πŸ“€
  • ABNF: Augmented Backus-Naur Form, a syntax for formally specifying syntax, defined in [RFC5234] and [RFC7405] 🧩

DANE is the DNS-Based Authentication of Named Entities (DANE) TLSA record [RFC7672], and it's kind of a cousin of MTA-STS πŸ‘― DANE is also built to upgrade unauthenticated encryption or plaintext transmission into authenticated, downgrade-resistant encrypted transmission. The catch is that DANE requires DNSSEC [RFC4033] for authentication. MTA-STS instead relies on certification authorities (CAs) and doesn't need DNSSEC, but that comes at the cost of risking malicious downgrades 😬 For a full breakdown of this trade-off, see Section 10, "Security Considerations" πŸ”

MTA-STS also has an optional testing-only mode, so soft deployments can catch policy failures before anything goes live πŸ§ͺ DANE can do partial deployments by putting TLSA records on only some of a domain's MXes, but that isn't possible for the per-domain policies MTA-STS uses πŸ™ˆ

The main reason MTA-STS exists is to give domains a way to make sure transport is secure, even when deploying DNSSEC is undesirable or impractical πŸ’– That said, MTA-STS is designed not to get in the way of DANE deployments when the two overlap. Especially, senders who implement MTA-STS validation MUST NOT let MTA-STS Policy validation override a failing DANE validation 🚫 Not me letting that slide!

3. Policy Discovery πŸŽ€

MTA-STS policies get distributed over HTTPS from a "well-known" [RFC5785] path served within the Policy Domain 🌐 Their presence and current version are shown by a TXT record at the Policy Domain πŸ“ These TXT records also hold a policy "id" field, so Sending MTAs can check whether a cached policy is still current without making an HTTPS request ⭐

To find out whether a recipient domain implements MTA-STS, a sender only needs to resolve a single TXT record πŸ™Œ And if a sender already has a cached policy for a domain and wants to know whether there's a newer one, it just checks the TXT record's version "id" against the cached value 🫢

3.1. MTA-STS TXT Records ✨

okay bestie, this is where the DNS side of things lives 🧚 The MTA-STS TXT record is a TXT record with the name "_mta-sts" sitting at the Policy Domain. So for "example.com", the record is "_mta-sts.example.com" πŸ’Œ MTA-STS TXT records MUST be US-ASCII, and they're semicolon-separated key/value pairs with these fields:

  • "v" (plaintext, required): right now, only "STSv1" is supported πŸ’…
  • "id" (plaintext, required): a short string used to track policy updates πŸ”₯ This string MUST uniquely identify a given instance of a policy, so senders can tell when the policy has changed by comparing it to the "id" of a policy they've already seen. There is no implied ordering of "id" fields between revisions, so don't go reading tea leaves into them πŸ™ˆ

here's an example TXT record:

_mta-sts.example.com.  IN TXT "v=STSv1; id=20160831085700Z;"

The formal definition of the "_mta-sts" TXT record, defined using ABNF [RFC7405], goes like this πŸŽ€

sts-text-record = sts-version 1*(sts-field-delim sts-field)
                  [sts-field-delim]

sts-field       = sts-id /                 ; Note that sts-id record
                  sts-extension            ; is required.

sts-field-delim = *WSP ";" *WSP

sts-version     = %s"v=STSv1"

sts-id          = %s"id=" 1*32(ALPHA / DIGIT)     ; id=...

sts-extension   = sts-ext-name "=" sts-ext-value  ; name=value

sts-ext-name    = (ALPHA / DIGIT)
                  *31(ALPHA / DIGIT / "_" / "-" / ".")

sts-ext-value   = 1*(%x21-3A / %x3C / %x3E-7E)
                  ; chars excluding "=", ";", SP, and CTLs

The TXT record MUST begin with the sts-version field, but the order of the other fields doesn't matter πŸ’œ If multiple TXT records for "_mta-sts" come back from the resolver, any record that doesn't begin with "v=STSv1;" gets tossed πŸ—‘οΈ If the number of resulting records is not one, or if the resulting record is syntactically invalid, senders MUST assume the recipient domain does not have an available MTA-STS Policy and skip the remaining steps of policy discovery. (Note that the absence of a usable TXT record is not by itself enough to wipe a sender's previously cached policy for the Policy Domain, as discussed in Section 5.1, "Policy Application Control Flow" πŸ¦‹) If the resulting TXT record contains multiple strings, the record MUST be treated as if those strings are concatenated without adding spaces.

The "_mta-sts" record MAY return a CNAME that points (directly or via other CNAMEs) to a TXT record, and in that case senders MUST follow the CNAME pointers πŸ”— This can be used for policy delegation, as described in Section 8.2 🌸

3.2. MTA-STS Policies πŸ“œ

The policy itself is a set of key/value pairs (kind of like header fields in [RFC5322]) served via the HTTPS GET method from the fixed "well-known" [RFC5785] path of ".well-known/mta-sts.txt", served by the Policy Host. The Policy Host DNS name is built by prepending "mta-sts" to the Policy Domain.

So for a Policy Domain of "example.com", the full URL is "https://mta-sts.example.com/.well-known/mta-sts.txt" 🌈

When fetching a policy, senders SHOULD validate that the media type is "text/plain". This guards against cases where web servers let untrusted users host non-text content (usually HTML or images) at a user-defined path πŸ™ˆ All parameters other than charset=utf-8 or charset=us-ascii are ignored. Additional "Content-Type" parameters are also ignored.

This resource contains the following CRLF-separated key/value pairs:

  • "version": right now, only "STSv1" is supported πŸ’…
  • "mode": One of "enforce", "testing", or "none", indicating the expected behavior of a Sending MTA when a policy validation fails. See Section 5, "Policy Application", for more details about the three modes ✨
  • "max_age": the max lifetime of the policy (plaintext non-negative integer seconds, maximum value of 31557600). Well-behaved clients SHOULD cache a policy for up to this value from the last policy fetch time. To mitigate the risks of attacks at policy refresh time, this value is expected to typically be in the range of weeks or greater πŸ“
  • "mx": Allowed MX patterns. One or more patterns matching allowed MX hosts for the Policy Domain. For example:
                  mx: mail.example.com <CRLF>
                  mx: *.example.net

this means mail for this domain might be handled by MX "mail.example.com" or any MX at "example.net". Valid patterns can be either fully specified names ("example.com") or suffixes prefixed by a wildcard ("*.example.net"). If a policy specifies more than one MX, each MX MUST have its own "mx:" key, and each MX key/value pair MUST be on its own line in the policy file. For Internationalized Domain Names [RFC5891], the "mx" value MUST specify the Punycode-encoded A-label [RFC3492] to match against, and NOT the Unicode-encoded U-label 🌍 The full semantics of certificate validation (including the use of wildcard patterns) are described in Section 4.1, "MX Host Validation" πŸ‘‘

An example policy looks like this 🧚

                  version: STSv1
                  mode: enforce
                  mx: mail.example.com
                  mx: *.example.net
                  mx: backupmx.example.com
                  max_age: 604800

The formal definition of the policy resource, defined using ABNF [RFC7405], is as follows πŸ’–

sts-policy-record        = sts-policy-field *WSP
                           *(sts-policy-term sts-policy-field *WSP)
                           [sts-policy-term]

sts-policy-field         = sts-policy-version /      ; required once
                           sts-policy-mode    /      ; required once
                           sts-policy-max-age /      ; required once
                           sts-policy-mx /
                           ; required at least once, except when
                           ; mode is "none"
                           sts-policy-extension      ; other fields

sts-policy-field-delim   = ":" *WSP

sts-policy-version     = sts-policy-version-field sts-policy-field-delim
                         sts-policy-version-value

sts-policy-version-field = %s"version"
sts-policy-version-value = %s"STSv1"

sts-policy-mode          = sts-policy-mode-field sts-policy-field-delim
                           sts-policy-mode-value

sts-policy-mode-field    = %s"mode"

sts-policy-mode-value    =  %s"testing" / %s"enforce" / %s"none"

sts-policy-mx            = sts-policy-mx-field sts-policy-field-delim
                           sts-policy-mx-value

sts-policy-mx-field      = %s"mx"

sts-policy-mx-value      = ["*."] Domain

sts-policy-max-age     = sts-policy-max-age-field sts-policy-field-delim
                         sts-policy-max-age-value

sts-policy-max-age-field = %s"max_age"

sts-policy-max-age-value = 1*10(DIGIT)

sts-policy-extension     = sts-policy-ext-name    ; additional
                           sts-policy-field-delim ; extension
                           sts-policy-ext-value   ; fields

sts-policy-ext-name      = (sts-policy-alphanum)
                           *31(sta-policy-alphanum / "_" / "-" / ".")

sts-policy-term          = LF / CRLF

sts-policy-ext-value     = sts-policy-vchar
                           [*(%x20 / sts-policy-vchar)
                           sts-policy-vchar]
                           ; chars, including UTF-8 [RFC3629],
                           ; excluding CTLs and no
                           ; leading/trailing spaces

sts-policy-alphanum     = ALPHA / DIGIT

sts-policy-vchar        = %x21-7E / UTF8-2 / UTF8-3 / UTF8-4

UTF8-2          =   <Defined in Section 4 of [RFC3629]>

UTF8-3          =   <Defined in Section 4 of [RFC3629]>

UTF8-4          =   <Defined in Section 4 of [RFC3629]>
Domain          =   <Defined in Section 4.1.2 of [RFC5321]>

Parsers MUST accept TXT records and policy files that are syntactically valid (i.e., valid key/value pairs separated by semicolons for TXT records), possibly containing additional key/value pairs not specified in this document. In that case, unknown fields SHALL be ignored. If any non-repeated field (i.e., all fields except "mx") is duplicated, all entries except for the first SHALL be ignored 🫢

3.3. HTTPS Policy Fetching πŸ›°οΈ

Policy bodies are, as described above, retrieved by Sending MTAs via HTTPS [RFC2818]. During the TLS handshake initiated to fetch a new or updated policy from the Policy Host, the Policy Host HTTPS server MUST present an X.509 certificate that is valid for the "mta-sts" DNS-ID [RFC6125] (e.g., "mta-sts.example.com") as described below, chains to a root CA that is trusted by the Sending MTA, and is non-expired. It's expected that Sending MTAs use a set of trusted CAs similar to those in widely deployed web browsers and operating systems. See [RFC5280] for more details about certificate verification πŸ”

The certificate is valid for the Policy Host (i.e., "mta-sts" prepended to the Policy Domain) with respect to the rules described in [RFC6125], with the following application-specific considerations:

  • Matching is performed only against the DNS-ID identifiers.
  • DNS domain names in server certificates MAY contain the wildcard character '*' as the complete left-most label within the identifier.

The certificate MAY be checked for revocation via the Online Certificate Status Protocol (OCSP) [RFC6960], certificate revocation lists (CRLs), or some other mechanism πŸ¦‹

Policies fetched via HTTPS are only valid if the HTTP response code is 200 (OK). HTTP 3xx redirects MUST NOT be followed, and HTTP caching (as specified in [RFC7234]) MUST NOT be used 🚫

Senders may wish to rate-limit the frequency of attempts to fetch the HTTPS endpoint, even if a valid TXT record for the recipient domain exists. In the case where the HTTPS GET fails, implementers SHOULD limit further attempts to a period of five minutes or longer per version ID, to avoid overwhelming resource-constrained recipients with cascading failures 🌧️

Senders MAY impose a timeout on the HTTPS GET and/or a limit on the maximum size of the response body, to avoid long delays or resource exhaustion during attempted policy updates. A suggested timeout is one minute, and a suggested maximum policy size is 64 kilobytes. Policy Hosts SHOULD respond to requests with a complete policy body within that timeout and size limit ⏱️

If a valid TXT record is found but no policy can be fetched via HTTPS (for any reason), and there is no valid (non-expired) previously cached policy, senders MUST continue with delivery as though the domain has not implemented MTA-STS πŸ“¬

Conversely, if no "live" policy can be discovered via DNS or fetched via HTTPS, but a valid (non-expired) policy exists in the sender's cache, the sender MUST apply that cached policy πŸ’œ

Finally, to mitigate the risk of persistent interference with policy refresh (discussed in-depth in Section 10), MTAs SHOULD proactively refresh cached policies before they expire. A suggested refresh frequency is once per day. To help administrators spot problems with policy refresh, MTAs SHOULD alert administrators (through logs or similar) when such attempts fail, unless the cached policy mode is "none" πŸ””

3.4. Policy Selection for Smart Hosts and Subdomains 🧚

okay so bestie, this one is about which policy a sender should look up. when you send mail through a "smart host" (an admin-configured middleman SMTP relay that is NOT the recipient's server as DNS says), compliant senders MUST treat the smart host's domain as the Policy Domain for policy discovery and application. πŸ’… this spec doesn't let you attach policies to email addresses that use Address Literals [RFC5321], so those are a no-go. πŸ™ˆ

then for subdomains: when you're emailing a mailbox at a subdomain, compliant senders MUST NOT try to fetch a policy from the parent zone. so for "user@mail.example.com", the policy can only be fetched from "mail.example.com", not "example.com". no sneaking up to mom's house for the policy πŸ’€βœ¨


4. Policy Validation πŸŽ€

when you're sending to an MX at a domain where the sender has a valid, non-expired MTA-STS Policy, a Sending MTA honoring MTA-STS MUST check whether:

  1. At least one of the policy's "mx" patterns matches the selected MX host, as described in Section 4.1, "MX Host Validation".
  2. The recipient mail server supports STARTTLS and offers a PKIX-based TLS certificate, during TLS handshake, which is valid for that host, as described in Section 4.2, "Recipient MTA Certificate Validation".

when those conditions aren't met, the policy is said to fail to validate. this section doesn't tell Sending MTAs what to do in that case. for that, go check Section 5, "Policy Application", which covers what happens when validation fails πŸ¦‹

4.1. MX Host Validation πŸ’–

a receiving candidate MX host is valid under an applied MTA-STS Policy if the MX record name matches one or more of the "mx" fields in that policy. matching follows the rules in [RFC6125], with one restriction: the wildcard character '' can only be used to match the entire left-most label in the presented identifier. so the mx pattern ".example.com" matches "mail.example.com", but NOT "example.com" or "foo.bar.example.com". nice try though πŸ™ƒ

4.2. Recipient MTA Certificate Validation πŸ”

the certificate the receiving MTA presents MUST not be expired, and it MUST chain to a root CA that the Sending MTA trusts. the certificate MUST have a subject alternative name (SAN) [RFC5280] with a DNS-ID [RFC6125] that matches the hostname, following the rules in [RFC6125]. the MX's certificate MAY also be checked for revocation via OCSP [RFC6960], CRLs [RFC6818], or some other mechanism. the vibes here are very "verify everything" 😭🫢


5. Policy Application πŸ‘‘

when sending to an MX at a domain where the sender has a valid, non-expired MTA-STS Policy, a Sending MTA honoring MTA-STS applies the result of a policy validation failure in one of two ways, depending on the policy's "mode" field:

  1. "enforce": in this mode, Sending MTAs MUST NOT deliver the message to hosts that fail MX matching or certificate validation, or that don't support STARTTLS. strict but iconic πŸ’…
  2. "testing": in this mode, Sending MTAs that also implement the TLSRPT (TLS Reporting) specification [RFC8460] send a report about policy application failures (as long as TLSRPT is also implemented by the recipient domain). either way, messages may be delivered as though there were no MTA-STS validation failure. πŸ§ͺ
  3. "none": in this mode, Sending MTAs should treat the Policy Domain as though it doesn't have any active policy. see Section 8.3, "Removing MTA-STS", for when to use this mode value 🧹

when a message fails to deliver because of an "enforce" policy, a compliant MTA MUST NOT permanently fail delivery before checking, via DNS, for an updated policy at the Policy Domain. (in all cases, MTAs SHOULD treat these failures as transient errors and retry delivery later.) this lets domains update long-lived policies on the fly, which is so smart ngl 🧠✨

5.1. Policy Application Control Flow 🌸

here's an example control flow for a compliant sender:

  1. Check for a cached policy whose time-since-fetch hasn't exceeded its "max_age". if none exists, attempt to fetch a new policy (perhaps asynchronously, so it doesn't block message delivery). Optionally, Sending MTAs may unconditionally check for a new policy at this step.
  2. For each candidate MX, in order of MX priority, attempt to deliver the message. if a policy is present with an "enforce" mode, then when attempting to deliver to each candidate MX, ensure STARTTLS support and host identity validity as described in Section 4, "Policy Validation". if a candidate fails validation, move on to the next candidate (if there is one).
  3. A message delivery attempt MUST NOT be permanently failed until the sender has first checked for the presence of a new policy (as indicated by the "id" field in the "_mta-sts" TXT record). if a new policy isn't found, the existing rules for temporary message delivery failures apply (as discussed in [RFC5321], Section 4.5.4.1). πŸ”

6. Reporting Failures πŸ“£

MTA-STS is meant to be used alongside TLSRPT [RFC8460] so implementing domains can spot both benign and malicious failures, and so that failures indicating an active attack are discoverable. πŸ•΅οΈβ€β™€οΈ because of that, senders that also implement TLSRPT SHOULD treat the following events as reportable failures:

  • HTTPS policy fetch failures when a valid TXT record is present.
  • Policy fetch failures of any kind when a valid policy exists in the policy cache, except if that policy's mode is "none".
  • Delivery attempts in which a contacted MX doesn't support STARTTLS or doesn't present a certificate that validates according to the applied policy, except if that policy's mode is "none".

7. Interoperability Considerations 🀝

7.1. SNI Support 🌈

to make sure the server sends the right certificate chain, the SMTP client MUST have support for the TLS Server Name Indication (SNI) extension [RFC6066]. when connecting to an HTTP server to retrieve the MTA-STS Policy, the SNI extension MUST contain the name of the Policy Host (e.g., "mta-sts.example.com"). when connecting to an SMTP server, the SNI extension MUST contain the MX hostname.

HTTP servers used to deliver MTA-STS policies MAY rely on SNI to figure out which certificate chain to present to the client. HTTP servers MUST respond with a certificate chain that matches the policy hostname, or abort the TLS handshake if they can't. clients that don't send SNI info may not see the expected certificate chain. πŸ™ƒ

SMTP servers MAY rely on SNI to determine which certificate chain to present to the client. however, servers with one identity and a single matching certificate don't need SNI support. Servers MUST NOT enforce the use of SNI by clients, since the client may be using unauthenticated opportunistic TLS and may not expect any particular certificate from the server. if the client sends no SNI extension, or sends one for an unsupported server name, the server MUST simply send a fallback certificate chain of its choice. the reason strict matching of the requested SNI hostname isn't enforced is that MTA-STS TLS clients are typically willing to accept multiple server names but can only send one name in the SNI extension. the server's fallback certificate might match a different name that the client accepts, e.g., the original next-hop domain. flexible but make it make sense πŸ’œ

7.2. Minimum TLS Version Support πŸ”’

MTAs supporting MTA-STS MUST have support for TLS 1.2 [RFC5246] or TLS 1.3 [RFC8446] or higher. the general TLS usage guidance in [RFC7525] SHOULD be followed. ✨


8. Operational Considerations πŸ› οΈ

8.1. Policy Updates πŸ”„

updating the policy means the owner has to make changes in two places: the "_mta-sts" TXT record in the Policy Domain's DNS zone, and the corresponding HTTPS endpoint. so recipients should expect that senders will keep using a policy until both the HTTPS and TXT endpoints are updated AND the TXT record's TTL has passed. πŸ•°οΈ

put another way, a sender who can't deliver a message while applying a cached copy of the recipient's now-outdated policy may not be able to discover that a new policy exists until the DNS TTL has passed. recipients SHOULD therefore make sure old policies keep working for message delivery during that window, or risk message delays. 😬

recipients SHOULD also update the HTTPS policy body before updating the TXT record. this ordering avoids the risk that senders, seeing a new TXT record, accidentally cache the old policy from HTTPS. order matters, bestie πŸŽ€

8.2. Policy Delegation πŸ¦‹

domain owners often delegate SMTP hosting to a different organization, like an ISP or a web host. in that case, they may also want to delegate the MTA-STS Policy to the same organization, which takes two changes.

first, the Policy Domain has to point its "_mta-sts" record, via CNAME, at the "_mta-sts" record maintained by the provider. this lets the provider control update signaling.

second, the Policy Domain has to point the "well-known" policy location at the provider. this can be done either by setting the "mta-sts" record to an IP address or CNAME the provider specifies, and giving the provider a TLS certificate that's valid for that host, or by setting up a "reverse proxy" (also known as a "gateway") server for the Policy Domain's Policy Host, configured to serve proxied responses from the provider's Policy Host. 🌐

here's an example where a user domain "user.example" is hosted by a mail provider "provider.example", and this configuration would allow policy delegation:

DNS:

        _mta-sts.user.example.  IN CNAME _mta-sts.provider.example.

Policy:

         > GET /.well-known/mta-sts.txt Host: mta-sts.user.example
         < HTTP/1.1 200 OK  # Response proxies content from
                            # https://mta-sts.provider.example

note that in all such cases, the policy endpoint ("https://mta-sts.user.example/.well-known/mta-sts.txt" in this example) must still present a certificate valid for the Policy Host ("mta-sts.user.example"), and NOT for that host at the provider's domain ("mta-sts.provider.example"). πŸ™…β€β™€οΈ

also, while Sending MTAs MUST NOT use HTTP caching when fetching policies via HTTPS, that caching may still be useful to a reverse proxy set up as described in this section. an HTTPS policy endpoint that expects to be proxied for multiple hosted domains (like a big mail hosting provider or similar) may want to send an HTTP Cache-Control "max-age" response directive (as specified in [RFC7234]) of 60 seconds. that's a reasonable value that saves reverse proxies from making an unnecessarily high rate of proxied policy fetches. the efficiency queen πŸ‘‘πŸ’…

8.3. Removing MTA-STS πŸ¦‹

Okay so this section is all about the clean exit! 🧚 MTA-STS has a "none" mode so a Policy Domain can say, super authoritatively, "we're done with MTA-STS πŸ’…." Then it can take down the TXT and policy endpoints for real later. The whole point is telling apart failures that mean an attack from ones that just mean someone opted out. iykyk πŸ™ˆ

Here's the suggested workflow, bestie:

  1. Publish a new policy with "mode" equal to "none" and a small "max_age" (e.g., one day). ✨
  2. Publish a new TXT record to trigger fetching of the new policy. πŸŽ€
  3. When all previously served policies have expired, safely remove the TXT record and HTTPS endpoint. 🧹 Normally that's the time the previously published policy was last served plus that policy's "max_age". But policies older than the previously published one might have been served with a greater "max_age" than it, so the caches can overlap πŸ‘€

8.4. Preserving MX Candidate Traversal πŸ“

Heads up for anyone building send-time MTA-STS validation into a mail transfer agent: be careful about messing with how you walk the MX candidate list! πŸ’€ An MTA-STS Policy can prefilter invalid MX candidates out of the list, so it's super tempting to do a "two-pass" model. You'd filter the candidates for validity against the policy first, then try whatever's left in order like there's no policy at all. But that can lead to incorrect implementations, like message loops 😭 The better move is to traverse the MX candidate list as usual and treat invalid candidates as though they were unreachable (aka as if there was some transient error trying to deliver to that candidate). 🫢

One consequence of checking MX hosts in normal candidate order: if a higher-priority MX is MTA-STS valid and a lower-priority MX isn't, senders may never even touch the lower-priority one. So policy misconfigs that only affect "backup" MXes might not show up until the primary goes down. Yikes 😬

9. IANA Considerations πŸ‘‘

9.1. Well-Known URIs Registry

A new "well-known" URI as described in Section 3 has been registered in the "Well-Known URIs" registry as described below:

  • URI Suffix: mta-sts.txt
  • Change Controller: IETF

9.2. MTA-STS TXT Record Fields

IANA has created a new registry titled "MTA-STS TXT Record Fields". The initial entries in the registry are:

       +------------+--------------------+-------------------------+
       | Field Name | Description        | Reference               |
       +------------+--------------------+-------------------------+
       | v          | Record version     | Section 3.1 of RFC 8461 |
       | id         | Policy instance ID | Section 3.1 of RFC 8461 |
       +------------+--------------------+-------------------------+

New fields are added to this registry using IANA's "Expert Review" policy [RFC8126]. πŸ’–

9.3. MTA-STS Policy Fields

IANA has created a new registry titled "MTA-STS Policy Fields". The initial entries in the registry are:

      +------------+----------------------+-------------------------+
      | Field Name | Description          | Reference               |
      +------------+----------------------+-------------------------+
      | version    | Policy version       | Section 3.2 of RFC 8461 |
      | mode       | Enforcement behavior | Section 3.2 of RFC 8461 |
      | max_age    | Policy lifetime      | Section 3.2 of RFC 8461 |
      | mx         | MX identities        | Section 3.2 of RFC 8461 |
      +------------+----------------------+-------------------------+

New fields are added to this registry using IANA's "Expert Review" policy. ✨

10. Security Considerations πŸ”’

Okay, this is the serious part, so pay attention πŸ™ˆ SMTP MTA-STS tries to protect against an active attacker who's trying to intercept or tamper with mail between hosts that support STARTTLS. There are two classes of attacks it considers:

  • Foiling TLS negotiation (for example, by deleting the 250 STARTTLS response from a server or altering TLS session negotiation). This would result in the SMTP session happening over plaintext, even though both parties support TLS. 😱
  • Impersonating the destination mail server. The sender might deliver the message to an impostor, who could then monitor and/or modify messages despite opportunistic TLS. This impersonation could be done by spoofing the DNS MX record for the recipient domain, or by redirecting client connections meant for the legitimate recipient server (for example, by altering BGP routing tables). πŸ•΅οΈ

MTA-STS can only thwart these attacks if the sender was able to obtain and cache a policy for the recipient domain beforehand, and only if the attacker can't obtain a valid certificate that complies with that policy. Let's look at specific attacks on this model πŸ‘€

10.1. Obtaining a Signed Certificate

SMTP MTA-STS relies on certificate validation via PKIX-based TLS identity checking [RFC6125]. So attackers who can get a valid certificate for the targeted recipient mail service (e.g., by compromising a CA) can get around STS authentication. πŸ’€

10.2. Preventing Policy Discovery

Since MTA-STS uses DNS TXT records for policy discovery, an attacker who can block DNS responses can suppress the discovery of an MTA-STS Policy. That makes the Policy Domain look like it has no MTA-STS Policy at all. The sender policy cache is designed to resist this by decreasing how often policy discovery happens, which shrinks the window of vulnerability. It's still a risk, though. Attackers who can predict or induce policy discovery, for example by getting a sending domain to email a never-before-contacted recipient during a man-in-the-middle attack, may be able to foil policy discovery and effectively downgrade the security of the delivery. 😀

Since this attack depends on intercepting initial policy discovery, implementers SHOULD prefer policy "max_age" values to be as long as is practical. ⏳

This attack is also possible when a cached policy gets refreshed, so implementers SHOULD NOT wait until a cached policy has expired before checking for an update. If senders refresh the cache regularly (for example, by fetching the current live policy in a background task that runs daily or weekly, no matter the state of the _mta-sts TXT record, and updating their cache's "max age" accordingly), an attacker would have to foil policy discovery consistently over the whole lifetime of a cached policy to stop a successful refresh. Main character behavior πŸ’…

Also, MTAs SHOULD alert administrators to repeated policy refresh failures long before cached policies expire (through warning logs or similar applicable mechanisms). That way admins can spot a persistent attack on policy refresh. 🚨 (However, they should not set up such alerts if the cached policy has a "none" mode, so that clean MTA-STS removal still works, as described in Section 8.3.) πŸ¦‹

Resisting downgrade attacks like this one is a feature of DANE, because DANE uses DNSSEC for policy discovery. That lets it authoritatively determine "lack of a record" even for non-participating recipients. ✨

10.3. Denial of Service

We also consider the Denial-of-Service risk posed by an attacker who can modify the DNS records for a recipient domain. Without MTA-STS, such an attacker can make a Sending MTA cache invalid MX records, but only for as long as the sending resolver caches those records. With MTA-STS, the attacker can also advertise a new, long "max_age" MTA-STS Policy with "mx" constraints that validate the malicious MX record. That makes senders cache the policy and refuse to deliver messages once the victim has resecured the MX records. Yikes 😭

This attack is partly mitigated by the victim domain's ability to publish a new policy at any time, which updates the cached malicious policy. Though this does require the victim domain to both obtain a valid CA-signed certificate and understand and properly configure MTA-STS. 🫢

We're also looking at domains that deliberately let untrusted users serve untrusted content on user-specified subdomains. In some cases (e.g., the service tumblr.com), this means HTTPS hosting of user-registered subdomains. In other cases (e.g., dynamic DNS providers), it means letting untrusted users register custom DNS records at the provider's domain. 🌈

In these cases, untrusted users might be able to serve custom content at the "mta-sts" host, including an illegitimate MTA-STS Policy. We believe this attack is much harder because the attacker would also need to serve the _mta-sts TXT record on the same domain. As far as we know, that's not something widely given to untrusted users. This attack is also mitigated by the victim domain's ability to fix an invalid policy at any future date. 🧚

10.4. Weak Policy Constraints

Even if an attacker can't modify a served policy, there's still potential for configurations that let attackers on the same domain receive mail for that domain. For example, an easy option when writing an MTA-STS Policy for example.com is to set mx to *.example.com. In that case, recipient domains have to consider the risk that any user with a valid hostname and CA-signed certificate (for example, dhcp-123.example.com) will count as a valid MX host for that domain, from the perspective of MTA-STS Policy validation. 😬

10.5. Compromise of the Web PKI System

A bunch of risks apply to the PKI system used for certificate authentication. That covers both the "mta-sts" HTTPS host's certificate and the SMTP servers' certificates. These risks are broadly applicable across the Web PKI ecosystem and aren't specific to MTA-STS. Still, they deserve some thought in this context 🧐

Broadly speaking, attackers may compromise the system by getting certificates under fraudulent circumstances (i.e., by impersonating the legitimate owner of the victim domain), by compromising a CA or Delegate Authority's private keys, by obtaining a legitimate certificate issued to the victim domain, and similar.

One approach web browsers commonly use to help mitigate some of these attacks is allowing revocation of compromised or fraudulent certificates via OCSP [RFC6960] or CRLs [RFC6818]. These mechanisms are trade-offs of their own and aren't universally implemented. We still recommend that implementers of MTA-STS implement whichever revocation mechanisms fit their implementations best. πŸ”

11. References πŸ“š

11.1. Normative References

okay so these are the must-read-first docs, bestie πŸ“šβœ¨ you literally can't fully get MTA-STS without them πŸ’…

11.2. Informative References

these ones are the background reading 🧠 super handy for context, not strictly required to build the thing 🌸


Appendix A. MTA-STS Example Record and Policy ✨

okay picture this: the owner of "example.com" wants to dip their toes into MTA-STS πŸ¦‹ without breaking anyone's mail yet. so they publish a policy in testing mode, which means senders get to send reports πŸ“¬ but the actual delivery stays exactly the same. the goal is to check that the MXes are who they say they are, that TLS is working properly, and that the certificates the recipient MX shows actually validate πŸ” super smart rollout, honestly πŸ’…

MTA-STS Policy indicator TXT RR:

    _mta-sts.example.com.  IN TXT "v=STSv1; id=20160831085700Z;"

MTA-STS Policy file served as the response body at "https://mta-sts.example.com/.well-known/mta-sts.txt":

                     version: STSv1
                     mode: testing
                     mx: mx1.example.com
                     mx: mx2.example.com
                     mx: mx.backup-example.com
                     max_age: 1296000

Appendix B. Message Delivery Pseudocode 🧚

this is the pseudocode for what a compliant Sending MTA does, basically the whole delivery flow in one place 🫢 quick heads up though: this version fetches the policy right in the delivery path, which might not be ideal in a real implementation. we expect some implementers to go for a background fetch instead, so delivery doesn't get blocked when there's no cached policy yet πŸ™ˆ

   func isEnforce(policy) {
     // Return true if the policy mode is "enforce".
   }

   func isNonExpired(policy) {
     // Return true if the policy is not expired.
   }

   func tryStartTls(connection) {
     // Attempt to open an SMTP STARTTLS connection with the MX.
   }

   func certMatches(connection, host) {

     // Assume a handy function to return if the server
     // certificate presented in "connection" is valid for "host".
   }

   func policyMatches(candidate, policy) {
     for mx in policy.mx {
       // Literal match.
       if mx == candidate {
         return true
       }
       // Wildcard matches only the leftmost label.
       // Wildcards must always be followed by a '.'.
       if mx[0] == '*' {
         parts = SplitN(candidate, '.', 2)  // Split on the first '.'.
         if len(parts) > 1 && parts[1] == mx[2:] {
           return true
         }
       }
     }
     return false
   }

   func tryDeliverMail(connection, message) {
     // Attempt to deliver "message" via "connection".
   }

   func tryGetNewPolicy(domain) {
     // Check for an MTA-STS TXT record for "domain" in DNS, and return
     // the indicated policy.
   }

   func cachePolicy(domain, policy) {
     // Store "policy" as the cached policy for "domain".
   }

   func tryGetCachedPolicy(domain) {
     // Return a cached policy for "domain".
   }

   func reportError(error) {
     // Report an error via TLSRPT.
   }

   func tryMxAccordingTo(message, mx, policy) {
     connection := connect(mx)
     if !connection {
       return false  // Can't connect to the MX, so it's not an MTA-STS
                     // error.

     }
     secure := true
     if !policyMatches(mx, policy) {
       secure = false
       reportError(E_HOST_MISMATCH)
     } else if !tryStartTls(connection) {
       secure = false
       reportError(E_NO_VALID_TLS)
     } else if !certMatches(connection, policy) {
       secure = false
       reportError(E_CERT_MISMATCH)
     }
     if secure || !isEnforce(policy) {
       return tryDeliverMail(connection, message)
     }
     return false
   }

   func tryWithPolicy(message, domain, policy) {
     mxes := getMxForDomain(domain)
     for mx in mxes {
       if tryMxAccordingTo(message, mx, policy) {
         return true
       }
     }
     return false
   }

   func handleMessage(message) {
     domain := ... // domain part after '@' from recipient
     policy := tryGetNewPolicy(domain)
     if policy {
       cachePolicy(domain, policy)
     } else {
       policy = tryGetCachedPolicy(domain)
     }
     if policy {
       return tryWithPolicy(message, domain, policy)
     }
     // Try to deliver the message normally (i.e., without MTA-STS).
   }

Contributors πŸ’–

big thanks to these lovely people who helped make this happen 🌈

   Wei Chuang
   Google, Inc.
   weihaw@google.com

   Viktor Dukhovni
   ietf-dane@dukhovni.de

   Markus Laber
   1&1 Mail & Media Development & Technology GmbH
   markus.laber@1und1.de

   Nicolas Lidzborski
   Google, Inc.
   nlidz@google.com

   Brandon Long
   Google, Inc.
   blong@google.com

   Franck Martin
   LinkedIn, Inc.
   fmartin@linkedin.com

   Klaus Umbach
   1&1 Mail & Media Development & Technology GmbH
   klaus.umbach@1und1.de

Authors' Addresses πŸŽ€

and these are the authors, the brains behind it all πŸ‘‘

   Daniel Margolis
   Google, Inc.

   Email: dmargolis@google.com


   Mark Risher
   Google, Inc.

   Email: risher@google.com


   Binu Ramakrishnan
   Oath, Inc.

   Email: prbinu@yahoo.com


   Alexander Brotman
   Comcast, Inc.

   Email: alex_brotman@comcast.com


   Janet Jones
   Microsoft, Inc.

   Email: janet.jones@microsoft.com
πŸ“¦ where this page comes from