Home/Blog/Cybersecurity/MTA-STS and TLS-RPT Guide: Enforcing Email Encryption in Transit
Cybersecurity

MTA-STS and TLS-RPT Guide: Enforcing Email Encryption in Transit

Implement MTA-STS (Mail Transfer Agent Strict Transport Security) and TLS-RPT to enforce TLS encryption for email in transit and gain visibility into encryption failures.

By Inventive Software
MTA-STS and TLS-RPT Guide: Enforcing Email Encryption in Transit

MTA-STS and TLS-RPT Guide: Enforcing Email Encryption in Transit

MTA-STS (Mail Transfer Agent Strict Transport Security) enforces TLS encryption for email in transit, preventing downgrade attacks. TLS-RPT provides visibility into TLS failures. Together, they secure email communication beyond what STARTTLS alone provides.

Why MTA-STS Matters

┌─────────────────────────────────────────────────────────────────────────────┐
│                    THE PROBLEM: STARTTLS DOWNGRADE ATTACKS                  │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  WITHOUT MTA-STS (Opportunistic TLS):                                       │
│                                                                             │
│  ┌─────────────┐    STARTTLS?    ┌──────────────┐    TLS OK    ┌─────────┐ │
│  │   Sender    │───────────────▶│   MITM       │─────────────▶│ Your MX │ │
│  │   Server    │                │   Attacker   │              │ Server  │ │
│  └─────────────┘                └──────────────┘              └─────────┘ │
│                                        │                                   │
│                                        │ Strips "250 STARTTLS"             │
│                                        │ from server response              │
│                                        ▼                                   │
│                                 ┌──────────────┐                           │
│                                 │ Sender sees  │                           │
│                                 │ "TLS not     │                           │
│                                 │ supported"   │                           │
│                                 │              │                           │
│                                 │ Email sent   │                           │
│                                 │ in PLAINTEXT │                           │
│                                 └──────────────┘                           │
│                                                                             │
│  ⚠️  Attacker reads and/or modifies email content!                          │
│                                                                             │
├─────────────────────────────────────────────────────────────────────────────┤
│                    THE SOLUTION: MTA-STS (Enforced TLS)                     │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  WITH MTA-STS:                                                              │
│                                                                             │
│  ┌─────────────┐  1. Fetch policy   ┌──────────────────────────────────┐   │
│  │   Sender    │───────────────────▶│ https://mta-sts.example.com/     │   │
│  │   Server    │                    │ .well-known/mta-sts.txt          │   │
│  └─────────────┘                    │                                  │   │
│        │                            │ version: STSv1                   │   │
│        │                            │ mode: enforce                    │   │
│        │                            │ mx: mail.example.com             │   │
│        │                            │ max_age: 604800                  │   │
│        │                            └──────────────────────────────────┘   │
│        │                                                                   │
│        │  2. Require TLS + valid cert                                      │
│        │     matching policy                                               │
│        ▼                                                                   │
│  ┌─────────────┐     TLS REQUIRED    ┌──────────────┐                     │
│  │   Sender    │─────────────────────│   MITM       │                     │
│  │   Server    │     CAN'T STRIP     │   Attacker   │                     │
│  └─────────────┘─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ▶│   BLOCKED!   │                     │
│        │                              └──────────────┘                     │
│        │                                                                   │
│        │  3. TLS connection directly                                       │
│        │     to legitimate MX                                              │
│        ▼                                                                   │
│  ┌─────────────┐     ENCRYPTED       ┌─────────────┐                      │
│  │   Sender    │════════════════════▶│   Your MX   │                      │
│  │   Server    │       TLS 1.3       │   Server    │                      │
│  └─────────────┘                     └─────────────┘                      │
│                                                                             │
│  ✅ Email encrypted, attacker cannot intercept!                             │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

MTA-STS Components

┌─────────────────────────────────────────────────────────────────────────────┐
│                         MTA-STS ARCHITECTURE                                │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  COMPONENT 1: DNS TXT Record                                                │
│  ┌───────────────────────────────────────────────────────────────────────┐ │
│  │ _mta-sts.example.com. IN TXT "v=STSv1; id=20250108120000"            │ │
│  └───────────────────────────────────────────────────────────────────────┘ │
│  Purpose: Signals MTA-STS is enabled, ID changes trigger policy refresh    │
│                                                                             │
│  COMPONENT 2: HTTPS Policy File                                             │
│  ┌───────────────────────────────────────────────────────────────────────┐ │
│  │ URL: https://mta-sts.example.com/.well-known/mta-sts.txt             │ │
│  │                                                                        │ │
│  │ Contents:                                                              │ │
│  │   version: STSv1                                                       │ │
│  │   mode: enforce                                                        │ │
│  │   mx: mail.example.com                                                 │ │
│  │   mx: mail2.example.com                                                │ │
│  │   max_age: 604800                                                      │ │
│  └───────────────────────────────────────────────────────────────────────┘ │
│  Purpose: Defines allowed MX hosts and policy strictness                   │
│                                                                             │
│  COMPONENT 3: TLS-RPT DNS Record (Optional but Recommended)                 │
│  ┌───────────────────────────────────────────────────────────────────────┐ │
│  │ _smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:[email protected]"│ │
│  └───────────────────────────────────────────────────────────────────────┘ │
│  Purpose: Receive reports about TLS connection successes and failures      │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Step-by-Step Implementation

Step 1: Verify Prerequisites

Before implementing MTA-STS:

┌─────────────────────────────────────────────────────────────────────────────┐
│                         PREREQUISITES CHECKLIST                             │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ☐ MX servers have valid TLS certificates                                  │
│    • Certificate matches MX hostname exactly                                │
│    • Certificate is not expired                                             │
│    • Certificate is issued by trusted CA (not self-signed)                 │
│    • Certificate chain is complete                                          │
│                                                                             │
│  ☐ MX servers support TLS 1.2 or higher                                    │
│    • TLS 1.0 and 1.1 are deprecated                                        │
│    • TLS 1.3 recommended if possible                                        │
│                                                                             │
│  ☐ Ability to host HTTPS content at mta-sts.yourdomain.com                 │
│    • Valid TLS certificate for mta-sts subdomain                           │
│    • Web server, CDN, or static hosting available                          │
│                                                                             │
│  ☐ DNS management access                                                    │
│    • Can add TXT records                                                    │
│    • Can add A/CNAME records for mta-sts subdomain                         │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Verify MX TLS Certificate:

# Check TLS certificate on MX server
openssl s_client -connect mail.example.com:25 -starttls smtp < /dev/null 2>/dev/null | \
  openssl x509 -noout -dates -subject

# Expected output:
# notBefore=Jan  1 00:00:00 2024 GMT
# notAfter=Dec 31 23:59:59 2025 GMT
# subject=CN = mail.example.com

Step 2: Create the Policy File

Create mta-sts.txt with your policy:

version: STSv1
mode: testing
mx: mail.example.com
mx: mail2.example.com
max_age: 86400

Policy Fields:

FieldRequiredDescription
versionYesAlways STSv1
modeYesnone, testing, or enforce
mxYesMX hostnames (one per line, can use wildcards)
max_ageYesPolicy cache time in seconds (max: 31557600)

Mode Options:

ModeBehavior
noneMTA-STS disabled, ignore policy
testingLog failures, deliver anyway
enforceReject delivery on TLS failure

Example Policies:

# Single MX server
version: STSv1
mode: enforce
mx: mail.example.com
max_age: 604800

# Multiple MX servers
version: STSv1
mode: enforce
mx: mail1.example.com
mx: mail2.example.com
mx: mail3.example.com
max_age: 604800

# Wildcard MX (Google Workspace, Microsoft 365)
version: STSv1
mode: enforce
mx: *.mail.google.com
mx: *.outlook.com
max_age: 604800

# Google Workspace specific
version: STSv1
mode: enforce
mx: aspmx.l.google.com
mx: *.googlemail.com
max_age: 604800

Step 3: Host the Policy File

The policy must be served at:

https://mta-sts.yourdomain.com/.well-known/mta-sts.txt

Option A: Cloudflare Pages

  1. Create a repository with:
.well-known/
  mta-sts.txt
  1. Connect to Cloudflare Pages
  2. Add custom domain: mta-sts.yourdomain.com

Option B: AWS S3 + CloudFront

# Create S3 bucket
aws s3 mb s3://mta-sts-example-com

# Upload policy file
aws s3 cp mta-sts.txt s3://mta-sts-example-com/.well-known/mta-sts.txt \
  --content-type "text/plain"

# Configure CloudFront with custom domain and ACM certificate

Option C: Nginx

server {
    listen 443 ssl http2;
    server_name mta-sts.example.com;

    ssl_certificate /etc/letsencrypt/live/mta-sts.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mta-sts.example.com/privkey.pem;

    location /.well-known/mta-sts.txt {
        root /var/www/mta-sts;
        default_type text/plain;
    }
}

Option D: GitHub Pages

  1. Create repository mta-sts
  2. Add file .well-known/mta-sts.txt
  3. Enable GitHub Pages
  4. Configure custom domain mta-sts.yourdomain.com
  5. Add DNS CNAME: mta-sts.yourdomain.com → youruser.github.io

Step 4: Add DNS Records

MTA-STS TXT Record:

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

The id field should be a unique identifier that changes when the policy changes (timestamp works well).

TLS-RPT TXT Record:

_smtp._tls.example.com.  IN  TXT  "v=TLSRPTv1; rua=mailto:[email protected]"

Or send reports to a web endpoint:

_smtp._tls.example.com.  IN  TXT  "v=TLSRPTv1; rua=https://report.example.com/tlsrpt"

MTA-STS Subdomain (A or CNAME):

; If hosting on same server
mta-sts.example.com.  IN  A  203.0.113.10

; If using CDN/external hosting
mta-sts.example.com.  IN  CNAME  yourcdn.example.net.

Step 5: Verify Configuration

Check DNS Records:

# Check MTA-STS TXT record
dig +short TXT _mta-sts.example.com
# Expected: "v=STSv1; id=20250108120000"

# Check TLS-RPT record
dig +short TXT _smtp._tls.example.com
# Expected: "v=TLSRPTv1; rua=mailto:[email protected]"

Verify Policy File:

# Fetch and verify policy
curl -sS https://mta-sts.example.com/.well-known/mta-sts.txt

# Expected output:
# version: STSv1
# mode: testing
# mx: mail.example.com
# max_age: 86400

Online Validators:

TLS-RPT Reports

Report Structure

TLS-RPT reports are JSON files containing TLS connection information:

{
  "organization-name": "Google LLC",
  "date-range": {
    "start-datetime": "2025-01-07T00:00:00Z",
    "end-datetime": "2025-01-08T00:00:00Z"
  },
  "contact-info": "[email protected]",
  "report-id": "2025-01-08T00:00:00Z_example.com",
  "policies": [
    {
      "policy": {
        "policy-type": "sts",
        "policy-string": [
          "version: STSv1",
          "mode: enforce",
          "mx: mail.example.com",
          "max_age: 604800"
        ],
        "policy-domain": "example.com",
        "mx-host": "mail.example.com"
      },
      "summary": {
        "total-successful-session-count": 1547,
        "total-failure-session-count": 3
      },
      "failure-details": [
        {
          "result-type": "certificate-expired",
          "sending-mta-ip": "209.85.220.41",
          "receiving-mx-hostname": "mail.example.com",
          "receiving-ip": "203.0.113.10",
          "failed-session-count": 2,
          "additional-information": "Certificate expired 2025-01-05"
        },
        {
          "result-type": "starttls-not-supported",
          "sending-mta-ip": "209.85.220.42",
          "receiving-mx-hostname": "mail.example.com",
          "receiving-ip": "203.0.113.11",
          "failed-session-count": 1
        }
      ]
    }
  ]
}

Failure Types

Result TypeDescriptionAction
certificate-expiredMX certificate expiredRenew certificate immediately
certificate-not-trustedUnknown CA or self-signedUse trusted CA certificate
certificate-host-mismatchCert doesn't match hostnameReissue cert with correct hostname
starttls-not-supportedServer doesn't support TLSEnable STARTTLS on mail server
validation-failureGeneral validation errorCheck certificate chain
sts-policy-invalidMalformed policy fileFix policy file syntax
sts-webpki-invalidPolicy host certificate issueFix mta-sts subdomain certificate
dns-errorDNS lookup failedCheck DNS configuration

Processing Reports

Simple Email Processing:

#!/usr/bin/env python3
"""Parse TLS-RPT reports from email"""

import json
import gzip
import email
import sys
from email import policy

def parse_tlsrpt(msg_file):
    with open(msg_file, 'rb') as f:
        msg = email.message_from_binary_file(f, policy=policy.default)

    for part in msg.walk():
        content_type = part.get_content_type()
        if content_type in ['application/json', 'application/tlsrpt+json',
                           'application/tlsrpt+gzip']:
            payload = part.get_payload(decode=True)

            # Handle gzip compression
            if content_type.endswith('+gzip') or payload[:2] == b'\x1f\x8b':
                payload = gzip.decompress(payload)

            report = json.loads(payload)
            analyze_report(report)

def analyze_report(report):
    print(f"Report from: {report.get('organization-name', 'Unknown')}")
    print(f"Date range: {report['date-range']['start-datetime']} - "
          f"{report['date-range']['end-datetime']}")

    for policy in report.get('policies', []):
        domain = policy['policy'].get('policy-domain', 'Unknown')
        summary = policy.get('summary', {})

        success = summary.get('total-successful-session-count', 0)
        failure = summary.get('total-failure-session-count', 0)

        print(f"\nDomain: {domain}")
        print(f"  Success: {success}, Failures: {failure}")

        for failure in policy.get('failure-details', []):
            print(f"  - {failure['result-type']}: {failure['failed-session-count']} sessions")
            print(f"    MX: {failure.get('receiving-mx-hostname', 'Unknown')}")

if __name__ == '__main__':
    parse_tlsrpt(sys.argv[1])

Using Third-Party Services:

Several services aggregate and visualize TLS-RPT reports:

  • Postmark - Free TLS-RPT processing
  • Report URI - TLS-RPT + DMARC aggregation
  • EasyDMARC - Full email security monitoring
  • URIports - TLS reporting service

Deployment Strategy

┌─────────────────────────────────────────────────────────────────────────────┐
│                      MTA-STS DEPLOYMENT TIMELINE                            │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  WEEK 1: Setup                                                              │
│  ┌───────────────────────────────────────────────────────────────────────┐ │
│  │ Day 1-2: Verify MX TLS certificates are valid                        │ │
│  │ Day 3-4: Create and host policy file (mode: testing)                 │ │
│  │ Day 5-7: Add DNS records, verify with online tools                   │ │
│  │                                                                        │ │
│  │ Policy: mode: testing, max_age: 86400 (1 day)                        │ │
│  └───────────────────────────────────────────────────────────────────────┘ │
│                                                                             │
│  WEEKS 2-4: Monitoring                                                      │
│  ┌───────────────────────────────────────────────────────────────────────┐ │
│  │ • Monitor TLS-RPT reports daily                                       │ │
│  │ • Investigate any failure-details                                     │ │
│  │ • Fix certificate or configuration issues found                       │ │
│  │ • Verify all MX servers are working correctly                        │ │
│  │                                                                        │ │
│  │ Policy: mode: testing, max_age: 86400 (1 day)                        │ │
│  └───────────────────────────────────────────────────────────────────────┘ │
│                                                                             │
│  WEEK 5: Enforcement Preparation                                            │
│  ┌───────────────────────────────────────────────────────────────────────┐ │
│  │ Day 1: Review all TLS-RPT reports - ensure near-zero failures        │ │
│  │ Day 2: Update policy to mode: enforce                                │ │
│  │ Day 3-7: Monitor closely for delivery issues                         │ │
│  │                                                                        │ │
│  │ Policy: mode: enforce, max_age: 86400 (1 day)                        │ │
│  └───────────────────────────────────────────────────────────────────────┘ │
│                                                                             │
│  WEEKS 6+: Production                                                       │
│  ┌───────────────────────────────────────────────────────────────────────┐ │
│  │ • Increase max_age to 604800 (1 week) or 1209600 (2 weeks)           │ │
│  │ • Continue monitoring TLS-RPT reports weekly                          │ │
│  │ • Set up automated alerting for failures                             │ │
│  │                                                                        │ │
│  │ Policy: mode: enforce, max_age: 604800 (1 week)                      │ │
│  └───────────────────────────────────────────────────────────────────────┘ │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Platform-Specific Configuration

Google Workspace

Google Workspace domains can use MTA-STS with Google's MX servers:

version: STSv1
mode: enforce
mx: aspmx.l.google.com
mx: alt1.aspmx.l.google.com
mx: alt2.aspmx.l.google.com
mx: alt3.aspmx.l.google.com
mx: alt4.aspmx.l.google.com
mx: *.aspmx.l.google.com
max_age: 604800

Or use wildcard:

version: STSv1
mode: enforce
mx: *.google.com
mx: *.googlemail.com
max_age: 604800

Microsoft 365

version: STSv1
mode: enforce
mx: *.mail.protection.outlook.com
max_age: 604800

Proofpoint

version: STSv1
mode: enforce
mx: *.pphosted.com
max_age: 604800

Self-Hosted

version: STSv1
mode: enforce
mx: mail.example.com
mx: backup-mail.example.com
max_age: 604800

Troubleshooting

Common Issues

IssueSymptomSolution
Policy not found"No MTA-STS policy"Verify HTTPS URL works, check DNS
Certificate mismatchTLS-RPT shows certificate-host-mismatchMX cert must match MX hostname
Policy fetch failssts-webpki-invalidFix mta-sts subdomain certificate
Invalid policy syntaxsts-policy-invalidCheck policy file format (no extra spaces)
DNS issuesPolicy ID mismatchUpdate DNS TXT record ID when policy changes

Debug Commands

# Test full MTA-STS flow
# 1. Check DNS TXT record
dig +short TXT _mta-sts.example.com

# 2. Verify policy is reachable
curl -v https://mta-sts.example.com/.well-known/mta-sts.txt

# 3. Check MX server TLS
openssl s_client -connect mail.example.com:25 -starttls smtp < /dev/null 2>/dev/null | \
  openssl x509 -noout -subject -dates -issuer

# 4. Verify certificate matches MX hostname
echo | openssl s_client -connect mail.example.com:25 -starttls smtp 2>/dev/null | \
  openssl x509 -noout -text | grep -A1 "Subject Alternative Name"

# 5. Check TLS-RPT record
dig +short TXT _smtp._tls.example.com

Policy Update Procedure

When updating your policy:

  1. Update the policy file at mta-sts.yourdomain.com
  2. Update DNS TXT record ID to force cache refresh:
_mta-sts.example.com.  IN  TXT  "v=STSv1; id=20250108150000"
  1. Wait for propagation (DNS TTL + sender cache)

Best Practices

Security Recommendations

  1. Start with testing mode - Monitor before enforcing
  2. Keep certificates updated - Automated renewal recommended
  3. Use short max_age initially - 86400 seconds for flexibility
  4. Monitor TLS-RPT reports - Set up automated alerting
  5. Document all MX servers - Ensure all are in policy
  6. Test certificate changes - Before renewal/replacement

Policy File Best Practices

# DO: Use exact hostnames when possible
mx: mail.example.com
mx: backup.example.com

# DO: Include all MX servers
mx: mx1.example.com
mx: mx2.example.com
mx: mx3.example.com

# CAUTION: Wildcards can be too permissive
mx: *.example.com

# DON'T: Forget backup MX servers
# (will cause mail rejection if primary fails)

# DON'T: Set max_age too high initially
# (makes fixing issues slow)

Automated Certificate Monitoring

#!/bin/bash
# Check MX certificate expiration

DOMAIN="example.com"
MX_SERVERS=$(dig +short MX $DOMAIN | awk '{print $2}')

for mx in $MX_SERVERS; do
  expiry=$(echo | openssl s_client -connect ${mx}:25 -starttls smtp 2>/dev/null | \
           openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)

  expiry_epoch=$(date -d "$expiry" +%s)
  now_epoch=$(date +%s)
  days_left=$(( ($expiry_epoch - $now_epoch) / 86400 ))

  echo "$mx: $days_left days until expiration"

  if [ $days_left -lt 30 ]; then
    echo "WARNING: Certificate for $mx expires in $days_left days!"
  fi
done

Tools

Don't wait for a breach to act

Get a free security assessment. Our experts will identify your vulnerabilities and create a protection plan tailored to your business.

Formal Security Models Explained: Bell-LaPadula, Biba, Clark-Wilson, and Beyond

Formal Security Models Explained: Bell-LaPadula, Biba, Clark-Wilson, and Beyond

Master the formal security models that underpin all access control systems. This comprehensive guide covers Bell-LaPadula, Biba, Clark-Wilson, Brewer-Nash, lattice-based access control, and how to choose the right model for your organization.

Biometric Authentication: Understanding FAR, FRR, and CER for Security Professionals

Biometric Authentication: Understanding FAR, FRR, and CER for Security Professionals

Master the critical metrics behind biometric authentication systems including False Acceptance Rate (FAR), False Rejection Rate (FRR), and Crossover Error Rate (CER). Learn how to evaluate, tune, and deploy biometric systems across enterprise, consumer, and high-security environments.

Database Inference & Aggregation Attacks: The Complete Defense Guide

Database Inference & Aggregation Attacks: The Complete Defense Guide

Learn how inference and aggregation attacks exploit aggregate queries and combined data to reveal protected information, and discover proven countermeasures including differential privacy, polyinstantiation, and query restriction controls.

NIST 800-88 Media Sanitization Complete Guide: Clear, Purge, and Destroy Methods Explained

NIST 800-88 Media Sanitization Complete Guide: Clear, Purge, and Destroy Methods Explained

Master NIST SP 800-88 Rev. 1 media sanitization methods including Clear, Purge, and Destroy. Covers SSD vs HDD sanitization, crypto erase, degaussing, regulatory compliance, and building a media sanitization program.

Physical Security & CPTED: The Complete Guide to Protecting Facilities, Data Centers, and Critical Assets

Physical Security & CPTED: The Complete Guide to Protecting Facilities, Data Centers, and Critical Assets

A comprehensive guide to physical security covering CPTED principles, security zones, access control, fire suppression, and environmental controls for protecting facilities and data centers.

Threat Modeling with STRIDE and DREAD: A Complete Guide to Proactive Security Architecture

Threat Modeling with STRIDE and DREAD: A Complete Guide to Proactive Security Architecture

Master threat modeling with STRIDE and DREAD frameworks to identify, classify, and prioritize security threats before they become vulnerabilities. This comprehensive guide covers data flow diagrams, mitigation mappings, MITRE ATT&CK integration, and building an enterprise threat modeling program.