Email Security: Authentication and Encryption Technologies

A comprehensive guide to email security technologies including SMTP over TLS, SMTP Auth, SPF, DKIM, S/MIME, and PGP, explaining their roles in authentication and encryption.

Email is widely used in both business and personal contexts, but its communications are exposed to various security risks. SMTP was designed in the early 1980s and has no built-in mechanism for verifying who a sender actually is. Much like a postal envelope where anyone can write any return address, the MAIL FROM command and the From: header are self-declared fields that the sending party is free to set to anything. To compensate for this structural weakness, the industry has layered technologies across three different levels: encrypting the communication path, authenticating the sender, and protecting message integrity. This article explains exactly what attack each technology defends against and how, verifying the core logic with runnable Python code.

Email Authentication

1. Communication Path Encryption

SMTP (Simple Mail Transfer Protocol) over TLS

SMTP is the protocol used for sending email, but by default, communication content is sent in plaintext. By using TLS (Transport Layer Security), SMTP communication can be encrypted, preventing eavesdropping and tampering by third parties on the communication path. In practice this is done via the STARTTLS command, which upgrades a plaintext SMTP session to TLS — an approach known as opportunistic TLS.

  • Note: TLS encryption is limited to protecting the communication path. Once the email reaches and is stored on the destination mail server, it is decrypted, so it cannot protect against unauthorized access to the mail server itself.
  • STARTTLS stripping attacks: Because STARTTLS is opportunistic (“use it if supported”), a man-in-the-middle attacker who strips or tampers with the STARTTLS command or its response on the wire can silently downgrade the session to plaintext without either party noticing. To close this gap, MTA-STS (RFC 8461) lets a sending domain fetch and cache, over HTTPS, a policy stating “this destination domain always requires TLS,” and DANE for SMTP (RFC 7672) uses DNSSEC-protected TLSA records to verify that the presented certificate is the correct one — both mitigate downgrade and MITM attacks.

2. Sender Mail Server Authenticates the Sender

SMTP Auth (SMTP Authentication)

SMTP Auth is a mechanism where the mail server authenticates that the sender (user) is a legitimate user when sending email. It uses username and password for authentication, preventing unauthorized users from using the mail server.

  • Authentication Method: Secure authentication methods that do not directly transmit the password, such as challenge-response, are often used.
  • Prerequisite: many SMTP Auth mechanisms transmit credentials in a form that can be trivially recovered, so on the submission port (587) it must always be paired with TLS (section 1’s STARTTLS). Protecting credentials alone without TLS still leaks the password to anyone eavesdropping on the wire.

3. Receiving Mail Server Authenticates the Sending Mail Server

These technologies aim to prevent sender domain spoofing (spoofed emails). Since SMTP itself has no way to verify a sender’s identity, these mechanisms borrow DNS as a separate, already-trusted channel to answer, after the fact: “was a message claiming to be from this domain really sent over this path, with this key?”

SPF (Sender Policy Framework)

SPF is a mechanism that verifies whether the source IP address of an email is from a legitimate sending server for that domain.

  • How it works: An SPF record (a DNS TXT record starting with v=spf1) listing the IP addresses authorized to send email is registered in the DNS server of the sending domain. When the receiving mail server receives an email, it looks up the domain written in the SMTP MAIL FROM (the envelope-from, also called the Return-Path), queries that domain’s SPF record, and checks whether the connecting IP address falls within the ranges listed there.
  • Why this stops spoofing: If an attacker spoofs a domain and sends MAIL FROM: attacker@example.com, the attacker does not have permission to modify example.com’s DNS, so they cannot register their own sending IP in example.com’s SPF record. The lookup will therefore always come back as a mismatch (fail/softfail), and only a legitimate sender can obtain pass. In other words, SPF delegates sender authentication to a separate trust foundation: “control of a domain’s DNS = control of the domain.”
  • Mechanisms: ip4:/ip6: (enumerate IP ranges directly), a/mx (the IPs pointed to by that domain’s A/MX records), include: (recursively pull in another domain’s SPF record — used for outsourced mail delivery services), and all (the catch-all final token).
  • Qualifiers (strength of the verdict): a symbol prefixing each mechanism changes the strength of the result: + (pass, the implicit default), - (fail, a hard fail recommending rejection), ~ (softfail, suspicious but not immediately rejected), ? (neutral, no verdict either way). The qualifier attached to all matters most: -all is a strict policy (“reject anything not explicitly listed”), while ~all is a gentler, transition-friendly policy (“flag non-matches as suspicious but still accept”). +all effectively disables SPF and is dangerous.
  • Limitation 1 — it verifies the envelope-from, not the visible From header: SPF authenticates the SMTP-level MAIL FROM domain, which is a completely different field from the From: header a mail client displays to the user. An attacker can use a different domain they legitimately control (and can pass SPF for) as the envelope-from, while forging only the visible From: header — so-called “friendly-from spoofing.” Closing this gap is exactly what DMARC (below) is for.
  • Limitation 2 — breaks under mail forwarding: When a recipient auto-forwards mail to another address, the forwarding server’s IP is not in the original SPF record, so re-verification at the final destination comes back fail/softfail. Because SMTP forwarding structurally replaces the source IP with the forwarder’s IP, this is a limitation baked into SPF’s own design (we reproduce this below with Python).
  • Limitation 3 — DNS lookup limit: recursive expansion via include: (and similar mechanisms) is capped at 10 DNS lookups; exceeding it makes SPF evaluation return permerror (RFC 7208). Domains that outsource to too many third-party senders can hit this ceiling.

DKIM (DomainKeys Identified Mail)

DKIM is a mechanism that attaches a digital signature to an email to verify that the sending domain is legitimate and that the email content has not been tampered with. This is a direct email application of the general principle of digital signatures — the asymmetry where anyone can verify with a public key a signature that only the private-key holder could have produced.

  • How it works: The sender hashes the headers it chooses to sign plus the body, signs the result with its own private key, and attaches it as a DKIM-Signature header. The corresponding public key is published as a DNS TXT record at selector._domainkey.domain (the selector is an identifier that enables key rotation, letting the same domain run multiple keys in parallel). The receiving mail server reads d= (the signing domain) and s= (the selector) from the DKIM-Signature header, fetches the public key from DNS, and verifies the signature.
  • Why the choice of signed headers (the h= tag) determines tamper resistance: the h= tag in the DKIM-Signature header lists the header names (from, to, subject, date, etc.) included in the signature. Any header not listed there is outside the signature’s protected scope and can be rewritten without invalidating the signature. For example, with h=from:to:date that omits subject, an attacker can rewrite Subject: into something like “pay immediately” while the signature stays perfectly valid. The body, by contrast, is always covered by bh= (the body hash), so body tampering is always detected regardless of the h= configuration. We confirm this asymmetry in the Python experiment below.
  • Canonicalization: because line endings and trailing whitespace can change in transit, DKIM defines two canonicalization modes before signing — simple (almost no tolerance for changes) and relaxed (tolerates minor changes like whitespace folding) — so that formatting drift introduced by forwarding doesn’t break verification.

DMARC (Domain-based Message Authentication, Reporting & Conformance)

SPF and DKIM share a common weakness: neither guarantees that the domain it verifies is the same as the domain in the From: header a human actually sees. SPF verifies the envelope-from domain; DKIM verifies the d= domain — both are different fields from the visible From: header. If an attacker legitimately passes SPF and DKIM under a domain they control, say evil-mailer.example, while forging only the visible From: header to impersonate a well-known company’s domain, SPF and DKIM will each individually report “valid.”

DMARC closes this gap by adding an extra check called alignment.

  • Alignment check: DMARC compares the domain verified by SPF (the envelope-from domain) and the domain verified by DKIM (the d= tag domain) against the domain in the From: header. There are two matching modes: strict requires an exact match, while relaxed (the default) only requires the Organizational Domain to match, tolerating differences in subdomains.
  • The condition for a DMARC pass: either “SPF passes AND is aligned with the From header” or “DKIM passes AND is aligned with the From header” is sufficient (an OR, not an AND). In other words, even if SPF and DKIM each individually pass, DMARC still fails if neither is aligned. This OR-of-aligned-passes is the core mechanism that catches friendly-from spoofing.
  • Policy: domain administrators publish a DMARC record (a TXT record at _dmarc.domain) declaring a policy — p=none (monitor only, take no action), p=quarantine (treat as spam), or p=reject (reject outright) — plus a pct= field for the percentage of mail the policy applies to. Most organizations start with p=none to collect aggregate reports (specified via rua=), enumerate every legitimate sending path, and only then progressively tighten the policy to quarantine and then reject.

The figure below shows how the individual SPF/DKIM verdicts and the DMARC alignment check diverge for a legitimate email versus a friendly-from spoofing attempt.

DMARC alignment: SPF and DKIM can each pass individually yet DMARC still fails if neither is aligned with the From domain. Left: a legitimate email where From/SPF/DKIM domains all match, resulting in a DMARC pass. Right: friendly-from spoofing where SPF/DKIM pass individually for the attacker’s own domain but neither aligns with the forged From header, resulting in a DMARC fail

Python verification: SPF matching logic

We implement SPF record parsing and IP matching, and check four scenarios: a legitimate sender, an outsourced sender, a spoofed sender, and SPF breaking under forwarding.

import ipaddress

# --- Minimal SPF record parser / matching logic ---
# Real SPF (RFC 7208) has many mechanisms (ip4/ip6/a/mx/include/exists) and
# +/-/~/? qualifiers; here we implement the most common subset: ip4 / include / all.

QUALIFIER_RESULT = {
    "+": "pass",      # explicit pass (also the implicit default when omitted)
    "-": "fail",       # hard fail: rejection recommended
    "~": "softfail",   # soft fail: suspicious but may still be accepted
    "?": "neutral",    # no verdict either way
}


def parse_spf(record, resolver):
    """Split an SPF record string into tokens.
    resolver: a dict mapping domain -> SPF record string, standing in for DNS lookups
    performed by include:.
    """
    assert record.startswith("v=spf1"), "An SPF record must start with v=spf1"
    tokens = record.split()[1:]
    return tokens


def check_spf(record, source_ip, resolver, _depth=0):
    """Determine whether the source IP matches the SPF record, returning a result
    string based on the matching mechanism's qualifier. include: is resolved
    recursively (capped to guard against loops/excessive recursion).
    """
    if _depth > 10:
        return "permerror"  # RFC 7208 also treats loops/over-recursion as permerror

    ip = ipaddress.ip_address(source_ip)
    tokens = parse_spf(record, resolver)

    for tok in tokens:
        qualifier = "+"
        if tok[0] in "+-~?":
            qualifier, tok = tok[0], tok[1:]

        if tok == "all":
            return QUALIFIER_RESULT[qualifier]

        if tok.startswith("ip4:") or tok.startswith("ip6:"):
            network = tok.split(":", 1)[1]
            if ip in ipaddress.ip_network(network, strict=False):
                return QUALIFIER_RESULT[qualifier]

        elif tok.startswith("include:"):
            included_domain = tok.split(":", 1)[1]
            included_record = resolver.get(included_domain)
            if included_record is None:
                continue  # an unresolvable include is skipped
            sub_result = check_spf(included_record, source_ip, resolver, _depth + 1)
            # include only counts as a match if the sub-record itself hits "all" and passes
            if sub_result == "pass":
                return QUALIFIER_RESULT[qualifier]
            # if sub_result is fail/softfail/neutral, treat include as a non-match and continue

    return "neutral"  # no mechanism matched and there is no all (RFC-wise, this is neutral)


# --- Scenario setup ---
# example.com's SPF record: own IP range + an outsourced mail delivery service (_spf.mailer.example)
resolver = {
    "_spf.mailer.example": "v=spf1 ip4:198.51.100.0/24 -all",
}
spf_record_example_com = "v=spf1 ip4:203.0.113.0/24 include:_spf.mailer.example ~all"

test_cases = [
    ("203.0.113.10", "Own mail server (legitimate IP)"),
    ("198.51.100.5", "Outsourced mail delivery service (legitimate IP, allowed via include)"),
    ("192.0.2.99", "Spammer spoofing the domain (unauthorized IP)"),
]

print("SPF record:", spf_record_example_com)
print()
for ip, label in test_cases:
    result = check_spf(spf_record_example_com, ip, resolver)
    print(f"Source IP {ip:15s} ({label:55s}) -> SPF result: {result}")

print()
print("--- Simulating SPF breaking under mail forwarding ---")
direct_ip = "203.0.113.10"
print(
    f"Direct delivery   (connecting IP={direct_ip}): "
    f"{check_spf(spf_record_example_com, direct_ip, resolver)}"
)
forwarder_ip = "192.0.2.55"  # a third-party forwarding server's IP (not in the SPF record)
print(
    f"Forwarded delivery (connecting IP={forwarder_ip}): "
    f"{check_spf(spf_record_example_com, forwarder_ip, resolver)}"
)

Execution result:

SPF record: v=spf1 ip4:203.0.113.0/24 include:_spf.mailer.example ~all

Source IP 203.0.113.10    (Own mail server (legitimate IP)                        ) -> SPF result: pass
Source IP 198.51.100.5    (Outsourced mail delivery service (legitimate IP, allowed via include)) -> SPF result: pass
Source IP 192.0.2.99      (Spammer spoofing the domain (unauthorized IP)          ) -> SPF result: softfail

--- Simulating SPF breaking under mail forwarding ---
Direct delivery   (connecting IP=203.0.113.10): pass
Forwarded delivery (connecting IP=192.0.2.55): softfail

Both the legitimate own-IP and outsourced-IP cases came back pass, and the spoofed, unauthorized IP came back softfail (a warning, not a rejection, per ~all). More importantly, the forwarding simulation shows that the exact same legitimate message flips from pass to softfail purely because the connecting IP changed at the forwarding hop. This is a structural limitation of SPF alone, and it’s exactly why production deployments pair SPF with DKIM — since DKIM signs the headers and body rather than depending on the connecting IP, it tends to survive forwarding intact.

Python verification: DKIM signing and tamper detection (the effect of the h= tag)

We sign a hash of email headers and body with RSA and verify how the choice of headers listed in h= changes tamper resistance.

import hashlib

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding, rsa

# --- A simplified DKIM implementation reproducing the RFC 6376 hash-and-sign flow ---
# Real DKIM uses "rsa-sha256" (RSA + PKCS#1 v1.5 padding) and signs only the headers
# listed in the DKIM-Signature header's h= tag. Canonicalization is simplified here
# to isolate the essential point: which headers are covered by the signature.

private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()


def canonicalize(headers: dict, signed_header_names: list[str], body: str) -> bytes:
    """Build the signed string: the signed headers (only those in h=) plus the body hash."""
    body_hash = hashlib.sha256(body.encode()).hexdigest()
    parts = [f"{name}:{headers[name]}" for name in signed_header_names]
    parts.append(f"body-hash:{body_hash}")
    return "\n".join(parts).encode()


def dkim_sign(headers: dict, signed_header_names: list[str], body: str) -> bytes:
    data = canonicalize(headers, signed_header_names, body)
    return private_key.sign(data, padding.PKCS1v15(), hashes.SHA256())


def dkim_verify(headers: dict, signed_header_names: list[str], body: str, signature: bytes) -> bool:
    data = canonicalize(headers, signed_header_names, body)
    try:
        public_key.verify(signature, data, padding.PKCS1v15(), hashes.SHA256())
        return True
    except Exception:
        return False


# --- The original email ---
original_headers = {
    "from": "billing@example.com",
    "to": "user@example.net",
    "subject": "Your invoice #1024",
    "date": "Fri, 17 Jul 2026 09:00:00 +0900",
}
original_body = "Please find attached your invoice for July. Total: $50."

print("=== Case A: h= includes only From/To/Date, Subject is not signed ===")
h_narrow = ["from", "to", "date"]  # subject deliberately omitted (a weak configuration)
sig_narrow = dkim_sign(original_headers, h_narrow, original_body)
print("Verification right after signing:", dkim_verify(original_headers, h_narrow, original_body, sig_narrow))

tampered_headers = dict(original_headers)
tampered_headers["subject"] = "URGENT: Your invoice #1024 - pay immediately via gift card"
print(
    "Verification after tampering the Subject (undetectable, since it's outside the signature):",
    dkim_verify(tampered_headers, h_narrow, original_body, sig_narrow),
)

print()
print("=== Case B: h= also includes Subject (the recommended configuration) ===")
h_wide = ["from", "to", "subject", "date"]
sig_wide = dkim_sign(original_headers, h_wide, original_body)
print("Verification right after signing:", dkim_verify(original_headers, h_wide, original_body, sig_wide))
print(
    "Verification after tampering the Subject (now detectable, since it is signed):",
    dkim_verify(tampered_headers, h_wide, original_body, sig_wide),
)

print()
print("=== Reference: body tampering is always detected (body-hash is always covered) ===")
tampered_body = original_body.replace("$50", "$50000")
print(
    "Verification after tampering the body (rejected even with Case A's signature, due to body-hash mismatch):",
    dkim_verify(original_headers, h_narrow, tampered_body, sig_narrow),
)

Execution result:

=== Case A: h= includes only From/To/Date, Subject is not signed ===
Verification right after signing: True
Verification after tampering the Subject (undetectable, since it's outside the signature): True

=== Case B: h= also includes Subject (the recommended configuration) ===
Verification right after signing: True
Verification after tampering the Subject (now detectable, since it is signed): False

=== Reference: body tampering is always detected (body-hash is always covered) ===
Verification after tampering the body (rejected even with Case A's signature, due to body-hash mismatch): False

In Case A, because Subject was excluded from the signature, rewriting it into a fraudulent line like “pay immediately via gift card” left the signature verification True — a realistic misconfiguration that phishing attacks can exploit. Case B shows that including Subject in h= reliably detects (False) the exact same tampering. Body tampering, on the other hand, is always caught via the body-hash mismatch regardless of the h= configuration. When configuring DKIM in production, it’s important to include every header that spoofing or tampering could actually weaponize — From, Subject, Date, To, and similar — in h= without omission.

Python verification: DMARC alignment logic

We implement the alignment check comparing the From domain, the SPF domain, and the DKIM domain, across three scenarios: a legitimate email, a case where relaxed alignment saves a legitimate outsourced flow, and friendly-from spoofing.

def registrable_domain(domain: str) -> str:
    """A simplified Organizational Domain extractor for relaxed alignment
    (production code should use a Public Suffix List). Here we just take the
    last two labels, e.g. 'a.b.example.com' -> 'example.com'.
    """
    labels = domain.split(".")
    return ".".join(labels[-2:]) if len(labels) >= 2 else domain


def is_aligned(from_domain: str, auth_domain: str, mode: str) -> bool:
    """DMARC alignment check.
    strict: the From domain and the authenticated domain must match exactly.
    relaxed: matching Organizational Domains is enough (tolerates subdomain differences).
    """
    if mode == "strict":
        return from_domain == auth_domain
    return registrable_domain(from_domain) == registrable_domain(auth_domain)


def dmarc_evaluate(from_domain, spf_domain, spf_result, dkim_domain, dkim_result, mode="relaxed"):
    """DMARC verdict: DMARC passes if at least one of SPF or DKIM (1) passed its own
    check AND (2) is aligned with the From domain. This directly implements RFC 7489's
    "either one is sufficient" OR structure.
    """
    spf_pass_aligned = (spf_result == "pass") and is_aligned(from_domain, spf_domain, mode)
    dkim_pass_aligned = (dkim_result == "pass") and is_aligned(from_domain, dkim_domain, mode)
    dmarc_pass = spf_pass_aligned or dkim_pass_aligned
    return {
        "spf_aligned": spf_pass_aligned,
        "dkim_aligned": dkim_pass_aligned,
        "dmarc_result": "pass" if dmarc_pass else "fail",
    }


scenarios = [
    dict(
        label="Legitimate: sent from and through the same organization's domain",
        from_domain="example.com",
        spf_domain="example.com",       # the envelope-from (Return-Path) domain
        spf_result="pass",
        dkim_domain="example.com",      # the DKIM-Signature d= tag
        dkim_result="pass",
    ),
    dict(
        label="Legitimate: sent via a newsletter delivery service (relaxed alignment on a subdomain)",
        from_domain="news.example.com",
        spf_domain="bounce.mailer-service.example",  # the delivery service's own envelope-from domain
        spf_result="pass",
        dkim_domain="example.com",       # DKIM is signed with the outsourcing company's own key (aligned)
        dkim_result="pass",
    ),
    dict(
        label="Spoofing: friendly-from spoofing (SPF/DKIM both legitimately pass for the attacker's own domain)",
        from_domain="example.com",                 # the visible From header impersonates the real domain
        spf_domain="evil-mailer.attacker-domain.com",  # the actual sender (envelope-from) is the attacker's domain
        spf_result="pass",                          # legitimately passes as the attacker's own domain
        dkim_domain="evil-mailer.attacker-domain.com",
        dkim_result="pass",                          # legitimately signed with the attacker's own key = DKIM also passes
    ),
]

for sc in scenarios:
    label = sc.pop("label")
    result = dmarc_evaluate(**sc)
    print(f"[{label}]")
    print(f"  From: {sc['from_domain']} / SPF domain: {sc['spf_domain']} (SPF={sc['spf_result']})"
          f" / DKIM domain: {sc['dkim_domain']} (DKIM={sc['dkim_result']})")
    print(f"  -> SPF aligned: {result['spf_aligned']}, DKIM aligned: {result['dkim_aligned']}"
          f", DMARC verdict: {result['dmarc_result']}")
    print()

Execution result:

[Legitimate: sent from and through the same organization's domain]
  From: example.com / SPF domain: example.com (SPF=pass) / DKIM domain: example.com (DKIM=pass)
  -> SPF aligned: True, DKIM aligned: True, DMARC verdict: pass

[Legitimate: sent via a newsletter delivery service (relaxed alignment on a subdomain)]
  From: news.example.com / SPF domain: bounce.mailer-service.example (SPF=pass) / DKIM domain: example.com (DKIM=pass)
  -> SPF aligned: False, DKIM aligned: True, DMARC verdict: pass

[Spoofing: friendly-from spoofing (SPF/DKIM both legitimately pass for the attacker's own domain)]
  From: example.com / SPF domain: evil-mailer.attacker-domain.com (SPF=pass) / DKIM domain: evil-mailer.attacker-domain.com (DKIM=pass)
  -> SPF aligned: False, DKIM aligned: False, DMARC verdict: fail

In the second scenario, the SPF domain (the delivery service’s own bounce domain) doesn’t exactly match the From domain (news.example.com), so it’s unaligned under strict; but the DKIM domain (signed with the outsourcing company’s own key, example.com) shares an Organizational Domain with the From header, so relaxed alignment succeeds and DMARC passes overall. This reflects a realistic requirement: legitimate third-party sending arrangements (newsletter platforms, etc.) shouldn’t be broken by DMARC. In the third scenario, friendly-from spoofing, both SPF and DKIM legitimately pass for the attacker’s own domain — yet because neither is aligned with the forged From header, the overall DMARC verdict is fail. This is exactly why DMARC can block the kind of spoofing that SPF and DKIM alone cannot.

4. Recipient Authenticates the Sender and Protects Email Content

These technologies aim to authenticate the email sender and protect the confidentiality and integrity of the email content. Where SPF/DKIM/DMARC protect the delivery path, these protect the message content itself, end to end.

S/MIME (Secure / Multipurpose Internet Mail Extensions)

S/MIME is a standard technology that uses public key cryptography to perform digital signatures and encryption on emails.

  • Digital Signature: Proves the sender’s identity and detects email tampering.
  • Encryption: Encrypts the email body with the recipient’s public key so that only the holder of the corresponding private key can read it (in practice, the body is encrypted with a symmetric cipher such as AES, and only that symmetric key is wrapped for the recipient using RSA/ECDH — a hybrid encryption scheme).

Using S/MIME requires a digital certificate (an X.509 certificate) issued by a trusted Certificate Authority (CA).

  • Verifying the certificate chain: an X.509 certificate forms a hierarchy — an end-entity certificate (binding a user’s public key to their email address) → an intermediate CA certificate → a root CA certificate. The receiving mail client walks the signature chain from the certificate attached to the signature up through its parent certificates, checking whether it eventually reaches a pre-installed, trusted root certificate in the OS or client. If verification fails at any single link in the chain, the entire chain is deemed untrustworthy. In addition, the certificate’s validity period and revocation status (checked via CRLs or OCSP) must also be verified. This is a centralized trust model that concentrates trust in a single apex: the root CA.
  • Limitations: even though the message body is encrypted, headers such as Subject:, To:, and From: (the metadata) are usually still delivered in plaintext, so the mere existence of a communication — who sent what to whom, and when — cannot be concealed. Continually obtaining and renewing certificates from a legitimate CA is also an ongoing operational cost shared by any PKI.

PGP (Pretty Good Privacy) / OpenPGP

PGP is software/specification (OpenPGP, RFC 4880) that, like S/MIME, uses public key cryptography to perform digital signatures and encryption on emails.

  • Web of Trust vs. PKI: where S/MIME adopts a hierarchical trust model (PKI) with a Certificate Authority at its apex, PGP adopts a decentralized trust model called the “Web of Trust.” Instead of a central certificate authority, users sign each other’s public keys, building a network of trust. A given key’s validity is judged by tracing “keys I have personally verified” or “keys signed by someone I trust.” This design avoids the single point of failure and the single object of trust that a CA represents, but in exchange it shifts the burden of judging key validity onto distributed, socially-driven verification acts by individual users (key-signing parties, for example) — a design that has long been criticized for the operational friction of bootstrapping and establishing key trust in practice.
  • Usage: The sender and recipient need to exchange public keys in advance.
  • An implementation caveat (EFAIL, 2018): EFAIL, disclosed in 2018, was a vulnerability affecting many mail client implementations of both PGP and S/MIME. It exploited the malleability of the block cipher modes (CBC/CFB, etc.) used for encryption, rewriting part of the ciphertext inside an HTML email so that the mail client’s automatic rendering of external resources (such as image tags) would exfiltrate the decrypted plaintext back to the attacker over a back channel. This was less a flaw in the cryptographic design of PGP/S/MIME as protocols and more a gap in integrity verification at the implementation layer — it reaffirmed the importance of using an MDC (Modification Detection Code) or authenticated encryption (AEAD) alongside body encryption, and of disabling automatic loading of external resources in HTML email.

BIMI (Brand Indicators for Message Identification)

BIMI lets domains that have achieved a sufficient level of DMARC enforcement (typically requiring p=quarantine or p=reject to be actively applied) display a brand logo in the recipient’s inbox. Historically this required obtaining a VMC (Verified Mark Certificate), which proves trademark ownership of the logo; in 2025, Google announced adoption of the CMC (Common Mark Certificate), which does not require a registered trademark (a year or more of demonstrated logo use is sufficient instead) — lowering the barrier to entry. According to one survey, among the top ten million domains, the number publishing a BIMI record grew from 14,305 in June 2023 to 21,222 in June 2024, though the same report notes that over 90% of domains still publish no BIMI record at all — so BIMI’s adoption as a DMARC endorsement mechanism is still in an early, developing stage.

AI/ML-based phishing detection

Since 2024, research applying large language models (LLMs) to phishing email detection has become considerably more active. Because LLMs can understand the semantic content of a message body, they are considered more robust against text that has been cleverly reworded to slip past keyword- or signature-based detection; researchers have reported hybrid approaches that combine LLMs with traditional machine learning models, as well as personalized detection using retrieval-augmented generation (RAG). At the same time, using an LLM as a standalone classifier has been shown to raise the false-positive rate — legitimate emails misclassified as phishing — so no standard, production-ready solution has fully emerged yet. There is also a growing recognition that the same generative capability can be used to automatically produce more natural and convincing phishing emails, making “AI used on both the offense and defense side” an important emerging theme in this space.

By combining these technologies, email communication security can be strengthened in multiple layers, reducing risks such as phishing scams, spam, and information leakage.

Summary

  • SMTP has no built-in way to verify a sender, so defenses must be layered across encrypting the path (TLS/STARTTLS), authenticating the sender (SPF/DKIM/DMARC), and protecting the content (S/MIME/PGP).
  • SPF matches the connecting IP against the envelope-from domain’s DNS record, relying on DNS administrative control as a separate trust foundation. It breaks under forwarding and does not verify the visible From header.
  • DKIM applies the general principle of digital signatures — private-key signing, public-key verification — to email; only the headers listed in the h= tag are protected from tampering (confirmed experimentally above).
  • DMARC adds an alignment check between the domains SPF/DKIM verify individually and the visible From header, catching friendly-from spoofing that SPF/DKIM alone cannot (confirmed experimentally above).
  • S/MIME relies on a PKI certificate chain rooted in a CA, while PGP relies on a decentralized Web of Trust — two different trust models for achieving end-to-end signing and encryption. Implementation flaws (EFAIL) are a real-world caveat for both.
  • Recently, BIMI is refining brand display, and research on LLM-based phishing detection is advancing, even as AI itself becomes a tool used on both the attacking and defending sides.

References

  • 2023 Information Security Specialist Exam Study Guide (Amazon.co.jp)
  • Wong, M., & Schlitt, W. (2014). “Sender Policy Framework (SPF) for Authorizing Use of Domains in Email”. RFC 7208.
  • Crocker, D., Hansen, T., & Kucherawy, M. (2011). “DomainKeys Identified Mail (DKIM) Signatures”. RFC 6376.
  • Kucherawy, M., & Zwicky, E. (2015). “Domain-based Message Authentication, Reporting, and Conformance (DMARC)”. RFC 7489.
  • Margolis, D., et al. (2018). “SMTP MTA Strict Transport Security (MTA-STS)”. RFC 8461.
  • Dukhovni, V., & Hardaker, W. (2015). “SMTP Security via Opportunistic DNS-Based Authentication of Named Entities (DANE)”. RFC 7672.
  • Ramsdell, B., & Turner, S. (2010). “Secure/Multipurpose Internet Mail Extensions (S/MIME) Version 3.2 Message Specification”. RFC 5751.
  • Callas, J., et al. (2007). “OpenPGP Message Format”. RFC 4880.
  • Poddebniak, D., et al. (2018). “Efail: Breaking S/MIME and OpenPGP Email Encryption using Exfiltration Channels”. USENIX Security Symposium 2018.
  • BIMI Group. “Brand Indicators for Message Identification — Supporting Documents”. bimigroup.org.
  • What is SPF? What is DKIM? What is DMARC? Explaining Email Authentication Technologies (SendGrid Blog)
  • What is S/MIME? What is PGP? Explaining Email Encryption and Signatures (SendGrid Blog)