Chapter 17: Email Security

"SMTP was designed in 1982 by people who trusted each other. We've been paying for that trust ever since."

It started with an invoice. The CFO of a mid-size manufacturing company received an email from their regular supplier, asking to update the bank account for future wire transfers. The email came from the right domain, had the right signature block, referenced a real purchase order number, and matched the writing style of the usual contact. The CFO updated the account details. Over the next three months, $2.4 million was wired to the attacker's account.

How did the email come from the right domain? The company had SPF and DKIM configured. They also had DMARC set to p=none -- monitor mode only, no enforcement. The attacker registered a lookalike domain (supplier-invoices.com instead of supplier.com), set up valid SPF and DKIM for their fake domain, and sent an email that passed every technical authentication check. Understanding how email security actually works, where it breaks down, and why Business Email Compromise is a $50-billion-per-year industry -- that is the subject of this chapter.


SMTP: Insecure by Design

The Simple Mail Transfer Protocol (SMTP, RFC 5321) is the foundation of email. Understanding its inherent insecurity is essential to understanding why we need SPF, DKIM, and DMARC -- and why even those are not enough.

SMTP was designed in 1982 (original RFC 821, by Jon Postel) for a small network of trusted university and government computers. Authentication was not a concern because everyone on ARPANET knew each other. Forty years later, that same protocol carries billions of messages per day across a global network full of adversaries.

How SMTP Works

graph LR
    subgraph Sending["Sending Side"]
        MUA1["Alice's Email Client<br/>(MUA)"]
        MTA1["smtp.a.com<br/>(Sending MTA)"]
    end

    subgraph Receiving["Receiving Side"]
        MTA2["smtp.b.com<br/>(Receiving MTA)"]
        MDA["Mail Delivery Agent"]
        MUA2["Bob's Inbox<br/>(MUA)"]
    end

    MUA1 -->|"SMTP (587)<br/>with authentication"| MTA1
    MTA1 -->|"SMTP (25)<br/>server-to-server<br/>NO sender auth!"| MTA2
    MTA2 --> MDA
    MDA -->|"IMAP/POP3"| MUA2

The critical vulnerability is in the server-to-server leg. When smtp.a.com connects to smtp.b.com to deliver mail, there is no standard mechanism for smtp.b.com to verify that the sending server is authorized to send mail on behalf of the claimed sender domain. SMTP trusts whatever the connecting server claims.

The Trust Problem: Raw SMTP in Action

Here is what a raw SMTP conversation looks like. Pay attention to how the server accepts any sender address without verification:

Observe raw SMTP to understand the spoofing problem (use a test environment):

\```bash
# Connect to a mail server (use YOUR OWN test server only)
# WARNING: Spoofing email to real addresses is illegal in many jurisdictions

openssl s_client -connect smtp.example.com:587 -starttls smtp -quiet

# SMTP conversation:
# S: 220 smtp.example.com ESMTP ready
# C: EHLO test.local
# S: 250-smtp.example.com Hello
# S: 250-SIZE 35882577
# S: 250-8BITMIME
# S: 250-AUTH PLAIN LOGIN          ← Server supports authentication
# S: 250-STARTTLS
# S: 250 OK
# C: AUTH PLAIN <base64 credentials>
# S: 235 Authentication successful
# C: MAIL FROM:<ceo@bigcorp.com>    ← ANYONE can put ANY address here!
# S: 250 OK                         ← Server accepts it without checking!
# C: RCPT TO:<employee@target.com>
# S: 250 OK
# C: DATA
# S: 354 Start mail input
# C: From: "CEO" <ceo@bigcorp.com>  ← Header From can ALSO be anything!
# C: To: employee@target.com
# C: Subject: Urgent wire transfer
# C:
# C: Please wire $50,000 to account XYZ immediately.
# C: .
# S: 250 OK, message queued

# There are TWO "From" addresses:
# 1. MAIL FROM (envelope sender) - used for delivery/bounces
# 2. From: header - displayed to the user in their email client
# Neither is verified by SMTP itself.
\```

The authenticated SMTP session (port 587 with AUTH) verifies that the connecting client is authorized to use THIS mail server. But it does not verify that the client is authorized to send as the claimed sender address. And on port 25 (server-to-server), there is typically no authentication at all. Any server on the internet can connect to your mail server and claim to be sending mail from anyone. That is why SPF, DKIM, and DMARC were invented -- to retroactively bolt authentication onto a protocol that never had it.


SPF: Sender Policy Framework

SPF (RFC 7208) allows domain owners to publish a DNS TXT record listing which IP addresses are authorized to send email on behalf of their domain.

How SPF Works

sequenceDiagram
    participant Sender as Sending Server<br/>IP: 209.85.220.41
    participant Receiver as Receiving Server
    participant DNS as DNS

    Sender->>Receiver: MAIL FROM: <alice@example.com><br/>(from IP 209.85.220.41)

    Receiver->>DNS: TXT record for example.com?
    DNS->>Receiver: "v=spf1 ip4:209.85.220.0/24<br/>include:_spf.google.com -all"

    Note over Receiver: Check: Is 209.85.220.41<br/>in 209.85.220.0/24?<br/>YES → SPF PASS

    Receiver->>Receiver: Accept email (SPF passed)

    Note over Receiver: If sender IP were 198.51.100.1:<br/>Not in ip4 range → check includes<br/>Not in Google's range → hit -all<br/>→ SPF FAIL (hard fail)

SPF Record Syntax Explained

Inspect and understand SPF records:

\```bash
# Check a domain's SPF record
dig +short example.com TXT | grep spf
# "v=spf1 ip4:203.0.113.0/24 include:_spf.google.com -all"

# Break down the syntax:
# v=spf1                          Version (always spf1)
# ip4:203.0.113.0/24              Allow this IPv4 CIDR range
# ip6:2001:db8::/32               Allow this IPv6 CIDR range
# a                               Allow IPs from domain's A/AAAA records
# mx                              Allow IPs from domain's MX records
# include:_spf.google.com         Recursively check Google's SPF record
# include:sendgrid.net            Recursively check SendGrid's SPF record
# redirect=other.com              Use other.com's SPF record instead
# -all                            HARD FAIL everything else (reject)
# ~all                            SOFT FAIL everything else (mark but accept)
# ?all                            NEUTRAL (no opinion on everything else)

# Follow the include chain for Google Workspace
dig +short google.com TXT | grep spf
# "v=spf1 include:_spf.google.com ~all"

dig +short _spf.google.com TXT
# "v=spf1 include:_netblocks.google.com include:_netblocks2.google.com
#  include:_netblocks3.google.com ~all"

dig +short _netblocks.google.com TXT
# "v=spf1 ip4:35.190.247.0/24 ip4:64.233.160.0/19 ip4:66.102.0.0/20
#  ip4:66.249.80.0/20 ip4:72.14.192.0/18 ip4:74.125.0.0/16 ..."

# Count DNS lookups in an SPF record (CRITICAL: max 10 allowed!)
# Each 'include', 'a', 'mx', 'redirect', and 'exists' counts as 1 lookup
# Exceeding 10 causes a permanent error (permerror) → SPF fails!

# Example: This SPF has too many lookups
# v=spf1 include:spf.protection.outlook.com    (1)
#        include:_spf.google.com               (2, but Google's record has 3 more)
#        include:sendgrid.net                  (6)
#        include:mailchimp.com                 (7)
#        include:freshdesk.com                 (8)
#        include:zendesk.com                   (9)
#        include:salesforce.com                (10)
#        include:helpscout.net                 (11) ← PERMERROR!
#        -all

# Solution: Use ip4/ip6 (no DNS lookup) or SPF flattening services
\```

SPF Limitations

**SPF has significant weaknesses that limit its effectiveness:**

1. **SPF checks the envelope sender (MAIL FROM), NOT the header From.** The user sees the `From:` header in their email client, not the envelope sender. An attacker can set `MAIL FROM: <anything@attacker.com>` (passes SPF for attacker.com) while setting `From: CEO <ceo@bigcorp.com>` in the header (what the user sees). SPF passes. The user is deceived. This is why DMARC's alignment check is essential.

2. **SPF breaks with email forwarding.** When mail is forwarded, the forwarding server's IP is not in the original domain's SPF record. Legitimate forwarded email fails SPF. This affects mailing lists, email forwarding services, and auto-forwarding rules. ARC (Authenticated Received Chain) was created to mitigate this.

3. **The 10-DNS-lookup limit.** Complex organizations using many SaaS email services (Google Workspace, SendGrid, Mailchimp, Salesforce, Zendesk, Freshdesk, HubSpot) quickly exceed the 10-lookup limit. Exceeding it causes SPF to permanently fail (permerror) for ALL email from the domain. This is a major operational headache.

4. **Overly permissive includes.** If your SPF includes a shared hosting provider's entire IP range (`include:shared-hosting.com`), anyone else on that hosting provider can pass your SPF check. Some shared email platforms have IP ranges covering millions of customers.

DKIM: DomainKeys Identified Mail

DKIM (RFC 6376) adds a cryptographic signature to email headers, proving that the message was authorized by the domain owner and has not been modified in transit. Unlike SPF (which validates the sending server's IP), DKIM validates the message content itself.

How DKIM Works

sequenceDiagram
    participant Sender as Sending MTA<br/>(smtp.example.com)
    participant DNS as DNS
    participant Receiver as Receiving MTA

    Note over Sender: 1. Select headers to sign (From, To, Subject, Date)
    Note over Sender: 2. Canonicalize (normalize whitespace, case)
    Note over Sender: 3. Hash the body (SHA-256)
    Note over Sender: 4. Sign hash + headers with PRIVATE key
    Note over Sender: 5. Add DKIM-Signature header

    Sender->>Receiver: Email with DKIM-Signature header:<br/>v=1; a=rsa-sha256; d=example.com;<br/>s=selector1; h=from:to:subject:date;<br/>bh=2jUSOH9N... (body hash)<br/>b=AuUoFEfD... (signature)

    Receiver->>DNS: TXT record for<br/>selector1._domainkey.example.com?
    DNS->>Receiver: "v=DKIM1; k=rsa;<br/>p=MIIBIjANBgkq..." (PUBLIC key)

    Note over Receiver: 6. Verify signature with public key
    Note over Receiver: 7. Recalculate body hash, compare with bh=
    Note over Receiver: 8. Result: DKIM PASS or FAIL

DKIM Record Inspection

Inspect DKIM records and signatures:

\```bash
# Find a domain's DKIM public key
# You need to know the selector (common values: google, selector1, s1, dkim, mail)
dig +short google._domainkey.gmail.com TXT
# "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."

dig +short selector1._domainkey.microsoft.com TXT
# "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQ..."

# To find the selector, look at the DKIM-Signature header in a received email:
# DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
#   d=example.com; s=selector1;
#                   ^^^^^^^^^^^^
#                   This is the selector
#   h=from:to:subject:date:message-id;
#   bh=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=;
#   b=dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSb/avSdGp...

# Decode and examine the body hash
echo "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=" | base64 -d | xxd
# This is the SHA-256 hash of the canonicalized email body

# Verify DKIM signature using opendkim tools
# Save the full email (with all headers) to a file, then:
opendkim-testmsg < email.eml && echo "DKIM PASS" || echo "DKIM FAIL"

# Using Python:
# pip install dkimpy
# python3 -c "
# import dkim
# with open('email.eml', 'rb') as f:
#     result = dkim.verify(f.read())
#     print('DKIM PASS' if result else 'DKIM FAIL')
# "
\```

DKIM Key Rotation

DKIM keys should be rotated regularly. The selector mechanism makes this straightforward:

  1. Generate a new key pair with a new selector name (e.g., selector2)
  2. Publish the new public key in DNS at selector2._domainkey.example.com
  3. Configure the mail server to sign with the new private key and selector
  4. After a transition period (long enough for all in-transit email to be delivered), remove the old DNS record
**DKIM implementation details:**

**Canonicalization** (`c=` tag) determines how headers and body are normalized before signing:
- `simple/simple`: No normalization. Any modification (even adding a trailing space) breaks the signature. Fragile but strict.
- `relaxed/relaxed`: Normalizes whitespace and header case. Tolerates minor modifications by mail servers. Recommended for production.

**Key sizes:**
- RSA-1024: Minimum allowed. Some services still use it. Vulnerable to offline factoring by well-funded adversaries.
- RSA-2048: Current standard. Provides adequate security for the foreseeable future.
- Ed25519: Newer, smaller keys (32 bytes vs 256 bytes for RSA-2048). Faster signing and verification. Defined in RFC 8463. Adoption is growing but not yet universal.

**DKIM replay attacks:** A legitimate DKIM-signed email can be re-sent to different recipients. The signature remains valid because the content has not changed. An attacker who obtains one legitimately signed email from your domain can send it to millions of recipients, and every copy will pass DKIM verification. This is a known limitation with no complete solution.

DKIM Strengths and Weaknesses

Strengths:

  • Survives email forwarding (the signature travels with the message, unlike SPF which checks the sending IP)
  • Proves the message body has not been modified in transit
  • Cryptographically strong (RSA-2048 or Ed25519)
  • Does not have the 10-lookup limit problem that plagues SPF

Weaknesses:

  • Only signs SELECTED headers. Headers not in the h= tag can be added or modified after signing.
  • Does NOT tell the receiver what to do with failures. DKIM says "this signature is valid/invalid" but does not specify policy. That is DMARC's job.
  • Subject to replay attacks (see above)
  • Broken by some mailing list software that modifies the body (adding footer text, wrapping lines)

DMARC: The Policy Layer

DMARC (RFC 7489) is the critical piece that ties SPF and DKIM together and tells receiving servers what to do when authentication fails. Without DMARC, SPF and DKIM are informational only -- they tell you whether authentication passed, but the receiving server has no guidance on what to do with failures.

The Alignment Problem DMARC Solves

The fundamental weakness of SPF and DKIM alone is that neither checks whether the authenticated domain matches what the user actually sees:

  • SPF authenticates the envelope sender (MAIL FROM). The user never sees this.
  • DKIM authenticates the signing domain (d= tag). The user might not notice this.
  • The user sees the From: header, which neither SPF nor DKIM directly validates.

DMARC introduces alignment: the domain that passes SPF or DKIM must match (or be a subdomain of) the domain in the visible From: header.

DMARC Validation Flow

graph TD
    A["Email arrives"] --> B["Check SPF"]
    A --> C["Check DKIM"]

    B --> D{"SPF result?"}
    D -->|"PASS"| E{"SPF aligned?<br/>Does MAIL FROM domain<br/>match From: header domain?"}
    D -->|"FAIL"| F["SPF not aligned"]
    E -->|"Yes"| G["DMARC: SPF aligned PASS"]
    E -->|"No"| F

    C --> H{"DKIM result?"}
    H -->|"PASS"| I{"DKIM aligned?<br/>Does DKIM d= domain<br/>match From: header domain?"}
    H -->|"FAIL"| J["DKIM not aligned"]
    I -->|"Yes"| K["DMARC: DKIM aligned PASS"]
    I -->|"No"| J

    G --> L{"Either aligned?"}
    K --> L
    F --> L
    J --> L

    L -->|"Yes (at least one)"| M["DMARC PASS<br/>Deliver normally"]
    L -->|"No"| N["DMARC FAIL"]
    N --> O{"DMARC policy?"}
    O -->|"p=none"| P["Deliver normally<br/>(report only)"]
    O -->|"p=quarantine"| Q["Send to spam folder"]
    O -->|"p=reject"| R["Reject email (5xx)"]

    style M fill:#44aa44,color:#fff
    style P fill:#ffaa00,color:#000
    style Q fill:#ff8800,color:#fff
    style R fill:#ff4444,color:#fff

DMARC's alignment requirement is the key insight. Without DMARC, an attacker could set MAIL FROM: <anything@attacker.com> (passes SPF for attacker.com) and From: ceo@bigcorp.com (what the user sees). SPF passes. DKIM passes (signed by attacker.com). But DMARC fails because the authenticated domains (attacker.com) do not align with the visible From domain (bigcorp.com). With p=reject, that email never reaches the inbox.

DMARC Record Examples

Inspect and understand DMARC records:

\```bash
# Check a domain's DMARC record (always at _dmarc.domain)
dig +short _dmarc.google.com TXT
# "v=DMARC1; p=reject; rua=mailto:mailauth-reports@google.com"

dig +short _dmarc.paypal.com TXT
# "v=DMARC1; p=reject; rua=mailto:d@rua.agari.com;
#  ruf=mailto:d@ruf.agari.com"

dig +short _dmarc.example.com TXT
# (may be empty if DMARC is not configured)

# DMARC record syntax:
# v=DMARC1              Version (required, must be first)
# p=none|quarantine|reject  Policy for the domain (required)
# sp=none|quarantine|reject  Policy for subdomains (optional)
# pct=100               Percentage of failures to apply policy to (0-100)
# rua=mailto:...        Aggregate report destination (daily XML reports)
# ruf=mailto:...        Forensic/failure report destination (per-failure)
# adkim=r|s             DKIM alignment mode: r=relaxed (subdomains OK), s=strict
# aspf=r|s              SPF alignment mode: r=relaxed, s=strict
# fo=0|1|d|s            Failure reporting options:
#                        0=both fail, 1=either fails, d=DKIM fail, s=SPF fail

# Example: Full enforcement with reporting
# v=DMARC1; p=reject; sp=reject; pct=100;
#   rua=mailto:dmarc-agg@example.com;
#   ruf=mailto:dmarc-forensic@example.com;
#   adkim=s; aspf=s; fo=1
\```
**The DMARC deployment journey:**

Jumping straight to `p=reject` without monitoring will break legitimate email from services you forgot about. Follow this proven path:

**Phase 1: Monitor (weeks 1-4)**
\```
v=DMARC1; p=none; rua=mailto:dmarc@example.com
\```
Collect aggregate reports. Discover all legitimate sending sources. You will be surprised -- marketing platforms, CRM systems, transactional email services, support ticketing systems, calendar invitations, and internal tools all send email as your domain.

**Phase 2: Partial quarantine (weeks 5-8)**
\```
v=DMARC1; p=quarantine; pct=10; rua=mailto:dmarc@example.com
\```
Start quarantining 10% of failures. Monitor for legitimate email being caught. Fix misconfigurations. Gradually increase `pct` to 25, 50, 100.

**Phase 3: Full quarantine (weeks 9-12)**
\```
v=DMARC1; p=quarantine; pct=100; rua=mailto:dmarc@example.com
\```
All DMARC failures go to spam. Watch for support tickets about missing emails. Fix remaining issues.

**Phase 4: Reject (week 13+)**
\```
v=DMARC1; p=reject; rua=mailto:dmarc@example.com; ruf=mailto:dmarc-forensic@example.com
\```
Full enforcement. Reject spoofed emails outright with a 5xx SMTP error.

**Aggregate reports (RUA)** are XML files sent daily by receiving mail servers. They contain:
- Which source IPs sent email claiming to be from your domain
- Whether those emails passed SPF, DKIM, and DMARC
- Volume counts for each source/result combination

Use tools like DMARCian, Valimail, Postmark's free DMARC monitoring, or open-source parsers (parsedmarc) to visualize these reports. Raw XML is nearly unreadable for humans.

How Phishing Bypasses SPF, DKIM, and DMARC

So if you have SPF, DKIM, and DMARC at p=reject, are you safe from phishing? Not even close. These technologies prevent direct domain spoofing -- an attacker cannot send email as @bigcorp.com and have it pass authentication. But attackers have evolved far beyond direct spoofing.

Lookalike Domains (Typosquatting)

Legitimate:  accounting@bigcorp.com
Lookalike:   accounting@bigc0rp.com    (zero instead of 'o')
Lookalike:   accounting@bigcorp.co     (missing 'm')
Lookalike:   accounting@bigcorp-inc.com (added suffix)
Lookalike:   accounting@blgcorp.com    ('l' instead of 'i')
Lookalike:   accounting@bigcorp.com    (Cyrillic 'a' U+0430 instead of Latin 'a')

Each lookalike domain is a real domain the attacker registers and configures with valid SPF, DKIM, and DMARC. The email passes ALL authentication checks because it IS legitimately from that domain. The deception is in the domain name itself being visually similar to the target.

Display Name Spoofing

From: "John Smith, CEO of BigCorp" <j.smith4872@gmail.com>

Many mobile email clients show only the display name, truncating or hiding the actual email address. The email is legitimately from Gmail with valid SPF/DKIM/DMARC, but the user sees "John Smith, CEO of BigCorp."

Compromised Accounts

If an attacker compromises a legitimate email account through credential stuffing, phishing, or a password reuse attack, all email sent from that account passes SPF, DKIM, and DMARC because it IS legitimate email from the authorized infrastructure. The email is indistinguishable from genuine communication.

Reply-To Manipulation

From: vendor@legitimate-company.com    ← Legit email, passes DMARC
Reply-To: vendor@attacker-domain.com   ← Where replies actually go

The From address passes all checks. But when the recipient hits "Reply," the response goes to the attacker's address. Most email clients do not prominently display the Reply-To when it differs from From.

The $2.4 million BEC attack from the chapter opening -- here is the full anatomy:

1. **Reconnaissance (2 months prior):** The attacker compromised a low-privilege email account at the victim company through credential stuffing (the employee reused their password from a breached fitness app). They used this access to study internal communications, learn invoice formats, identify key financial contacts, and harvest real purchase order numbers.

2. **Domain registration:** Registered `supplier-invoices.com` (the real supplier was `supplier.com`). Set up proper SPF, DKIM, and DMARC for the fake domain.

3. **Staging:** Created an email account `accounting@supplier-invoices.com`. Replicated the exact email formatting, signature block, logo, and writing style of the real supplier.

4. **Execution:** Sent a single email to the CFO from `accounting@supplier-invoices.com` referencing a real purchase order, with the supplier's real bank being "updated" to the attacker's bank account.

5. **Result:** The email passed every technical authentication check. The CFO did not notice the domain was `supplier-invoices.com` instead of `supplier.com`. Three wire transfers over three months totaling $2.4 million.

**What would have prevented it:**
- Domain monitoring (detect lookalike registrations using services like DNSTwist)
- Out-of-band verification policy (call the supplier at a known phone number before changing bank details)
- Multi-person authorization for wire transfer changes above a threshold
- External email banner ("This message originated outside your organization")
- Training CFO to check the actual email address, not just the display name

STARTTLS and Its Weaknesses

STARTTLS: Opportunistic Encryption

STARTTLS upgrades a plaintext SMTP connection to encrypted TLS mid-conversation. It is called "opportunistic" because it is not mandatory -- if either side does not support it or if a middlebox strips the STARTTLS capability advertisement, the email falls back to plaintext.

sequenceDiagram
    participant S as Sending MTA
    participant R as Receiving MTA

    S->>R: EHLO sender.com
    R->>S: 250-smtp.receiver.com<br/>250-SIZE 35882577<br/>250-STARTTLS<br/>250 OK

    S->>R: STARTTLS
    R->>S: 220 Go ahead

    Note over S,R: TLS Handshake<br/>(certificate exchange, key negotiation)

    S->>R: EHLO sender.com (over TLS now)
    S->>R: MAIL FROM / RCPT TO / DATA (encrypted)

The STARTTLS Stripping Attack

sequenceDiagram
    participant S as Sending MTA
    participant MITM as Man-in-the-Middle
    participant R as Receiving MTA

    S->>MITM: EHLO sender.com
    MITM->>R: EHLO sender.com

    R->>MITM: 250-STARTTLS<br/>250 OK
    MITM->>S: 250 OK<br/>(STARTTLS removed!)

    Note over S: "Server doesn't support TLS.<br/>Fall back to plaintext."

    S->>MITM: MAIL FROM / DATA (PLAINTEXT!)
    Note over MITM: Read and/or modify email content
    MITM->>R: MAIL FROM / DATA (forwards to receiver)
**STARTTLS vulnerabilities:**

1. **Downgrade attacks:** A man-in-the-middle removes the "250-STARTTLS" line from the server's capability advertisement, causing the sender to transmit in plaintext. The sender does not know encryption was available.

2. **No certificate validation:** Most SMTP implementations do NOT validate the receiving server's TLS certificate. They accept self-signed, expired, wrong-hostname, and even revoked certificates. This means a MITM can present any certificate and the connection will still be "encrypted" -- but to the attacker, not the real server.

3. **No policy mechanism:** There is no standard way for a domain to declare "you MUST use TLS to deliver email to me." SMTP senders simply try STARTTLS and fall back to plaintext if it fails. This is unlike HTTPS, where HSTS forces TLS.

Research from 2015 found that in several countries, over 20% of inbound email connections had STARTTLS stripped by network intermediaries (ISPs, government firewalls), downgrading connections to plaintext.

MTA-STS: Mandatory Email Encryption

MTA-STS (RFC 8461) solves the STARTTLS downgrade problem. It is the email equivalent of HSTS.

sequenceDiagram
    participant S as Sending MTA
    participant DNS as DNS
    participant Web as HTTPS Web Server<br/>(mta-sts.example.com)
    participant R as Receiving MTA

    S->>DNS: TXT record for _mta-sts.example.com?
    DNS->>S: "v=STSv1; id=20240101"

    S->>Web: GET https://mta-sts.example.com/.well-known/mta-sts.txt
    Web->>S: version: STSv1<br/>mode: enforce<br/>mx: mx1.example.com<br/>mx: mx2.example.com<br/>max_age: 604800

    Note over S: Policy says: MUST use TLS<br/>MUST validate certificate<br/>matches mx1 or mx2.example.com<br/>If TLS fails, DO NOT deliver in plaintext

    S->>R: EHLO (STARTTLS, TLS handshake with cert validation)
    S->>R: Deliver email (encrypted, authenticated)

    Note over S: Cache policy for 604800 seconds (7 days)<br/><br/>If a MITM strips STARTTLS, sending MTA<br/>refuses to deliver rather than downgrading

MTA-STS is exactly analogous to HSTS. Just as HSTS tells browsers to always use HTTPS, MTA-STS tells mail servers to always use TLS with certificate validation. And like HSTS, it has a TOFU (Trust On First Use) problem -- the very first fetch of the policy could be intercepted. But once cached, downgrades are prevented for the duration of max_age.


Email Header Forensics

When investigating a suspicious email, the headers tell the real story. Here is how to read them like a forensic investigator.

Analyze a real email header step by step:

\```bash
# View full headers:
# Gmail: Open email → Three dots → "Show original"
# Outlook: Open email → File → Properties → Internet Headers
# Apple Mail: View → Message → All Headers

# Example headers (read BOTTOM to TOP for chronological order):

# ---- Bottom (oldest, sent first) ----
# Return-Path: <bounce-12345@marketing.example.com>
#   → The envelope sender. Where bounces go.
#   → Note: this is marketing.example.com, not example.com

# Received: from mail-out.marketing.example.com (198.51.100.25)
#   by mx.google.com with ESMTPS id abc123
#   for <user@gmail.com>;
#   Wed, 12 Mar 2026 10:30:15 -0700 (PDT)
#   → Google's mail server received this from 198.51.100.25
#   → This is the most trustworthy Received header (your provider added it)

# Received: from internal-relay.marketing.example.com (10.0.1.5)
#   by mail-out.marketing.example.com (198.51.100.25) with SMTP
#   → Internal relay within the sender's infrastructure
# ---- Top (newest, added last) ----

# Authentication-Results: mx.google.com;
#   dkim=pass header.i=@example.com header.s=google;
#   spf=pass (google.com: domain of bounce-12345@marketing.example.com
#       designates 198.51.100.25 as permitted sender)
#       smtp.mailfrom=marketing.example.com;
#   dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=example.com
#   → THIS IS THE VERDICT. Google checked everything.
#   → dkim=pass: signature verified
#   → spf=pass: IP is authorized for marketing.example.com
#   → dmarc=pass: domains are aligned

# DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
#   d=example.com; s=google;
#   h=from:to:subject:date:message-id;
#   bh=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=;
#   b=base64-signature-data...
#   → Signed by example.com using selector "google"

# From: Alice Smith <alice@example.com>
#   → What the user sees. The display address.

# Reply-To: alice@completely-different-domain.com
#   → RED FLAG! If this differs from From:, investigate.

# X-Originating-IP: [198.51.100.25]
#   → Sometimes reveals the actual sending machine

# Things to check for suspicious emails:
# 1. Does Authentication-Results show dkim=pass, spf=pass, dmarc=pass?
# 2. Does Return-Path domain match From: domain? (DMARC alignment)
# 3. Does Reply-To differ from From:? (potential BEC indicator)
# 4. Do Received headers show expected infrastructure?
#    (check IPs with dig -x for reverse DNS)
# 5. Are there unusually few Received headers? (possible header injection)
# 6. Does the X-Mailer or User-Agent look legitimate?

dig -x 198.51.100.25 +short
# mail-out.marketing.example.com.
# Good: reverse DNS matches the claimed sending server
\```

Business Email Compromise (BEC)

BEC is the most financially damaging form of cybercrime, and it is growing. The FBI's Internet Crime Complaint Center (IC3) reports:

  • 2023: $2.9 billion in reported BEC losses in the US alone
  • 2022: $2.7 billion
  • 2021: $2.4 billion
  • Total 2016-2023: Over $51 billion globally (FBI estimate)

These numbers represent only reported losses. The actual figure is estimated to be 3-5 times higher because many organizations do not report BEC incidents.

BEC Attack Patterns

graph TD
    subgraph BEC_Types["Common BEC Scenarios"]
        CEO["CEO Fraud<br/>'I'm in a meeting, wire $45K<br/>to this account urgently'<br/>Target: Finance team"]
        VIF["Vendor Invoice Fraud<br/>'We updated our bank details,<br/>send future payments here'<br/>Target: Accounts payable"]
        ATT["Attorney Impersonation<br/>'Confidential acquisition,<br/>wire escrow funds to...'<br/>Target: C-suite, legal"]
        DATA["Data Theft<br/>'Send all W-2 forms<br/>for the audit'<br/>Target: HR, payroll"]
        GIFT["Gift Card Scam<br/>'Buy 10 Apple gift cards,<br/>send me the codes'<br/>Target: Admin staff"]
    end

    CEO --> LOSS1["Median loss: $75,000"]
    VIF --> LOSS2["Median loss: $125,000<br/>Highest single loss: $60M"]
    ATT --> LOSS3["Median loss: $150,000"]
    DATA --> LOSS4["Tax fraud, identity theft"]
    GIFT --> LOSS5["Median loss: $2,000"]

    style LOSS2 fill:#ff4444,color:#fff

BEC Defense: Technical + Process + Human

Technical controls (SPF/DKIM/DMARC) are necessary but insufficient. They prevent direct domain spoofing. They do not prevent lookalike domains, compromised accounts, or social engineering. BEC defense requires three layers.

**A comprehensive BEC defense strategy:**

**Technical Controls:**
1. DMARC at `p=reject` for your domain (prevents direct spoofing of your domain)
2. Lookalike domain monitoring (services like DNSTwist, PhishLabs, or Bolster detect typosquatting registrations within hours)
3. Advanced email filtering with ML-based content and behavior analysis
4. Banner warnings on ALL external emails: "This message originated outside your organization. Be cautious with links and attachments."
5. Link rewriting and attachment sandboxing (Proofpoint, Mimecast, Microsoft Defender for O365)
6. Anomaly detection: flag emails where From name matches an internal executive but comes from an external domain

**Process Controls:**
7. **Out-of-band verification** for any financial change: call the requester at a known phone number (NOT the number in the email!) to confirm
8. **Dual authorization** for wire transfers above a threshold ($5,000 is common)
9. **Written procedures** for updating vendor bank details (require signed forms, verification through an existing relationship contact, and a waiting period)
10. **Cooling-off period** for urgent financial requests: "If someone says it's urgent and you can't verify, wait 24 hours"

**Human Controls:**
11. Regular BEC-focused training with realistic simulations
12. Culture that encourages verification: "It is always OK to double-check, even if it appears to come from the CEO"
13. Reward reporting of suspicious emails; never punish false positives
14. Executive impersonation awareness: train C-suite that their names are the most common bait

Advanced Email Authentication: ARC and BIMI

ARC (Authenticated Received Chain)

ARC (RFC 8617) solves the "forwarding breaks authentication" problem. When a legitimate intermediary (like a mailing list or email forwarding service) forwards email, SPF fails (wrong IP) and DKIM may break (body modified by adding list footers). ARC preserves the original authentication results through a chain of cryptographic seals.

sequenceDiagram
    participant Sender as alice@example.com
    participant List as Mailing List<br/>(list@lists.org)
    participant Receiver as bob@company.com

    Sender->>List: Email (SPF pass, DKIM pass, DMARC pass)

    Note over List: Adds [LIST] prefix to Subject<br/>Adds list footer to body<br/>Forwards to all subscribers

    Note over List: SPF will fail (IP is lists.org, not example.com)<br/>DKIM will fail (body was modified)<br/><br/>WITHOUT ARC: DMARC fails → email rejected<br/>WITH ARC: List adds ARC headers preserving<br/>original authentication results

    List->>Receiver: Forwarded email with ARC headers:<br/>ARC-Authentication-Results: SPF=pass, DKIM=pass<br/>ARC-Message-Signature: (seal of the message)<br/>ARC-Seal: (signature over the ARC set)

    Note over Receiver: SPF fails (IP is lists.org)<br/>DKIM fails (body modified)<br/>But ARC chain is valid:<br/>lists.org attests that original auth passed<br/>Receiver trusts lists.org as an intermediary<br/>→ Deliver normally

BIMI (Brand Indicators for Message Identification)

BIMI displays a brand's verified logo next to authenticated emails in supporting email clients. It requires DMARC at p=quarantine or p=reject -- creating a positive incentive for email security adoption.

Requirements:

  1. DMARC at p=quarantine or p=reject (demonstrates email security maturity)
  2. Published BIMI DNS record with logo URL: default._bimi.example.com TXT "v=BIMI1; l=https://example.com/logo.svg; a=https://example.com/vmc.pem"
  3. Verified Mark Certificate (VMC) from a participating Certificate Authority (DigiCert)
  4. SVG Tiny 1.2 format logo

BIMI is essentially a visual reward for doing all the email security right. It creates a positive feedback loop: users learn to trust emails with the brand logo and be suspicious of emails without it. It is one of the few email security mechanisms that provides visible, user-facing value -- making security improvements that are normally invisible actually noticeable to end users.


Email Security Checklist

Outbound (Protecting Your Domain from Being Spoofed)

□ SPF record published with -all (hard fail)
□ SPF record stays within 10 DNS lookup limit
□ DKIM signing enabled with RSA-2048 or Ed25519 keys
□ DKIM selectors rotated at least annually
□ DMARC record published with rua= for aggregate reporting
□ DMARC policy at p=reject (after monitoring period)
□ Subdomain policy (sp=) set to match or be stricter than domain policy
□ MTA-STS policy published and in enforce mode
□ TLSRPT (TLS Reporting) configured for delivery failure visibility
□ BIMI record configured (where supported by recipients)
□ All legitimate sending services (marketing, CRM, ticketing) properly authenticated

Inbound (Protecting Your Users from Spoofed Email)

□ SPF validation enabled on receiving servers
□ DKIM verification enabled
□ DMARC enforcement enabled (honor sender's published policies)
□ DNSSEC validation for DNS lookups (prevents poisoned SPF/DKIM/DMARC records)
□ TLS enforced for inbound connections where possible
□ External email banner/warning enabled on all external messages
□ Link rewriting and click-time URL scanning
□ Attachment sandboxing (detonate suspicious attachments in isolated environment)
□ Lookalike domain detection and blocking
□ Advanced threat protection (ML-based content analysis)
□ User-reported phishing workflow with automated analysis and response
□ ARC validation for forwarded email

What You've Learned

  • SMTP has no built-in sender authentication -- both the envelope sender (MAIL FROM) and the visible header (From:) can be set to any address, which is why email spoofing has been possible since the protocol's creation in 1982
  • SPF publishes authorized sending IPs in DNS TXT records, but only validates the envelope sender, breaks with forwarding, has a 10-DNS-lookup limit, and is weakened by overly permissive includes
  • DKIM cryptographically signs email headers and body, proving message integrity and domain authorization -- it survives forwarding but is vulnerable to replay attacks and does not specify enforcement policy
  • DMARC ties SPF and DKIM together with an alignment check (the authenticated domain must match the visible From: header) and provides policy enforcement (none for monitoring, quarantine for spam folder, reject for outright rejection)
  • Even with perfect SPF/DKIM/DMARC at p=reject, phishing persists through lookalike domains, display name spoofing, compromised accounts, and Reply-To manipulation -- technical controls prevent direct domain spoofing but not social engineering
  • STARTTLS provides opportunistic SMTP encryption but is vulnerable to stripping attacks where a MITM removes the TLS capability advertisement; MTA-STS makes TLS mandatory with certificate validation, analogous to HSTS for the web
  • Email header forensics reveals the true authentication results, sending path, and potential manipulation -- read Received headers bottom-to-top, check Authentication-Results for the definitive verdict, and compare Return-Path and Reply-To against the From: address
  • Business Email Compromise causes over $2.9 billion in annual reported US losses and requires layered defense: technical controls (DMARC), process controls (out-of-band verification, dual authorization), and human training -- no single layer is sufficient
  • DMARC deployment must be gradual: start at p=none to discover all legitimate senders, progress through p=quarantine with increasing pct, and only move to p=reject after confirming all authorized email passes authentication
  • ARC preserves authentication results through email forwarding chains, and BIMI provides visual brand verification in email clients as a reward for proper email security implementation