Published on

Setting up MTA-STS in Exchange Online

Authors
  • avatar
    Name
    Jonathan Devere-Ellery
    Twitter
mailboxes
Photo by Yannik Mika on Unsplash

Microsoft announced back in February the general availability of SMTP MTA Strict Transport Security (MTA-STS) for emails in Exchange Online. This means that outbound emails from tenants are automatically protected using MTA-STS and with no configuration needed by administrators.

For inbound emails to Exchange Online however, this does require some additional configuration and is what we will explain in this article. To implement we need to publish two different components in order for remote email servers to ensure emails sent to Exchange Online and encrypted.

  1. Publish a TXT record in DNS
  2. Publish a Policy file specifying the MX records and TLS hostnames allowed to receive SMTP messages

What is MTA-STS?

First of all we should explain, what is MTA-STS, and why is it important?

Opportunistic TLS (STARTTLS)1 was created to better secure emails sent by SMTP, but this is still vulnerable to several different types of attacks.

Firstly, STARTTLS is completely optional so if an attacker can man-in-the-middle this connection process then they can effectively downgrade the security by stripping STARTTLS and preventing the negotiation of TLS encryption from succeeding, and the email to be sent in cleartext.

Secondly, an attacker could poison DNS responses and alter the MX responses for a domain, and cause sending email servers to connect to an attacker controlled SMTP server. The session would be encrypted, but email would be sent to an attacker-controlled server and certificate. SMTP does not include any way for a sending mail server to validate that an email is being sent to the correct SMTP server.

MTA-STS aims to be a simple way to validate that an email is being sent to the correct email server and that TLS is enforced. If the hostname of the receving SMTP server does not match the MX record published in the MTA-STS Policy file, or the server does not have a valid TLS certificate matching this hostname, then MTA-STS validation will fail and the sender will not send the email.

Implementing MTA-STS

Creating an MTA-STS Record in DNS

First we need to create a TXT record in DNS which advertises to other email servers that MTA-STS is available for this domain. The domain will always be in the format of _mta-sts.<domain.tld>

This record should contain two different fields:

  1. v=STSv1 which will always be the same value. Note that this is case-sensitive2
  2. id= field which can contain any alphanumeric value up to 32 characters long, and should be update every time the MTA-STS policy is updated. For simplicity I'd recommend using a datestamp to indicate the last time it was updated.

To create a record in DNS using PowerShell we could use:

$STSRecord = "v=STSv1; id=$(Get-Date -UFormat +%Y%m%d%H%M%SZ)"
Add-DnsServerResourceRecord -Name "_mta-sts" -Txt -DescriptiveText $STSRecord -ZoneName "contoso.com"

Hosting an MTA-STS Policy file

Unlike Sender Authentication (SPF, DKIM, DMARC), MTA-STS is not done entirely through DNS records, and we also have to publish a policy file on a webserver at a specific URL. This policy will contain details of which enforcement mode, details of which MX records / TLS hostnames are allowed to receive emails, and how long the policy file should be cached.

The policy file always needs to be hosted at https://mta-sts.<domain.tld>/.well-known/mta-sts.txt. Note that this is mta-sts.domain needs to be an A/AAAA record in DNS. If you use a CNAME record this will fail MTA-STS validation checks in Exchange Online.

Unfortunately it's not possible to host this file directly using Exchange Online, so a seperate solution needs to be used. If you already have an existing IIS or webserver for your organisation, then you can go ahead and use that, but in my case I don't have a webserver and the MTA-STS.txt file is only ~0.5kb. I don't want to create and manage a seperate webserver VM in my lab especially for such a tiny requirement.

Update (01/Oct/2022): I have written a Bicep template to automate the creation and configuration of an Azure Function App to host the mta-sts.txt file. If you are interested you can find the post at https://techobsessed.blog/blog/deploy-azure-function-app-using-bicep-for-mtasts/

There are other solutions for hosting this file, such as using GitHub Pages, but I decided on using Cloudflare Workers which fits my requirements nicely. By creating a Worker I can run Javascript code at the edge using the Cloudflare CDN, and use this to generate my static policy file.

From the Cloudflare Dashboard and Workers I created a new service. After creating the new service, clicking Quick Edit allows me to enter the following code, and then Save and Deploy the service.

async function handleRequest(request) {
  const init = {
    headers: {
      'content-type': 'text/plain; charset=UTF-8',
      'X-Clacks-Overhead': 'GNU Terry Pratchett',
    },
  }
  return new Response(stsHTML, init)
}
addEventListener('fetch', event => {
  return event.respondWith(handleRequest(event.request))
})
const stsHTML = `version: STSv1
mode: testing
mx: *.mail.protection.outlook.com
max_age: 86400
`

We also need to add some triggers to the service so that it will actually respond back on the URL that we need. We can achieve this by going under the Triggers heading, and adding a Route:

cloudflare-route1

From here we can add a new route of *<domain.tld>/.well-known/mta-sts.txt as a trigger for this service. We can also add multiple domains to all respond back with the same policy file if we want.

cloudflare-route2

Now when we make a request to the https://mta-sts.contoso.com/.well-known/mta-sts.txt we get a text/plain2 response back with the following content:

mta-sts.txt
version: STSv1
mode: testing
mx: *.mail.protection.outlook.com
max_age: 86400

If we have a TLS-RPT record configured in DNS, then we will start receiving reports of TLS usage when connecting on SMTP. We can use these reports to validate that our MTA-STS policy is working correctly. Once the validation is complete, we can change the policy mode from 'testing' to 'enforce' which actually begins protecting our domains. We should also increase the max_age value to a higher value to ensure that remote SMTP servers cache the policy for a longer period.

async function handleRequest(request) {
  const init = {
    headers: {
      'content-type': 'text/plain;charset=UTF-8',
      'X-Clacks-Overhead': 'GNU Terry Pratchett',
    },
  }
  return new Response(stsHTML, init)
}
addEventListener('fetch', event => {
  return event.respondWith(handleRequest(event.request))
})
const stsHTML = `version: STSv1
mode: enforce
mx: *.mail.protection.outlook.com
max_age: 604800
`

And that is it. We can continue to monitor the usage of TLS in SMTP by using TLS-RPT.

Hope that is helpful!

Other resources

Footnotes

  1. Secure SMTP over Transport Layer Security defined in RFC3207

  2. Requirements for MTA-STS response defined in RFC 8461 2