UnveilTech

UnveilScan Blog

← All articles

Try UnveilScan free

TLS 1.3 0-RTT: when fast becomes dangerous

Posted 2026-04-29 · 8 min read · TLSadvanced

TLS 1.3 introduced 0-RTT (zero round-trip time) resumption: a returning client can attach application data to its ClientHello, saving one full RTT. CDNs love the latency win and turn it on enthusiastically. The catch: 0-RTT data is, by design, replayable. Any active attacker who can capture it can replay it to the same server, possibly multiple times, possibly to a different node. RFC 8446 spells this out in §8 — most operators don't read §8.

The attack model in one diagram

Client            Attacker             Server
  |                  |                    |
  | --- early data --X--- captured -----> |   (legitimate, 1st delivery)
  |                  |                    |
  |                  | --- replayed ----> |   (attacker, 2nd delivery)
  |                  | --- replayed ----> |   (attacker, 3rd delivery)
  |                  | --- different node-> |  (attacker, geo-distributed)

What the attacker captures: encrypted bytes. They cannot read the request body. They can resend it as-is. The server has no cryptographic way to distinguish the replay from the original.

Why GET-only isn't a defense

The standard mitigation in CDN docs reads "0-RTT is safe for idempotent requests, so limit it to GET." This is wrong twice over:

Cloudflare, AWS, Azure: what's on by default

Provider0-RTT defaultNotes
CloudflareOff (opt-in via dashboard)Limited to GET/HEAD when on; documented replay risk
AWS CloudFrontOff; requires explicit security policyTLS 1.3 enabled but 0-RTT requires custom config
Azure Front DoorOffNo public toggle as of 2026-04
FastlyOpt-in per serviceDocuments single-use guarantee with caveats
nginx (with BoringSSL)Off; ssl_early_data onYou must add $ssl_early_data protection

If you've never explicitly enabled it, you're likely safe by default. The risk concentrates in two places: (a) teams who turned it on chasing latency wins without reading the spec; (b) custom Go/Rust services using tls.Config{MaxEarlyData: ...}.

Detecting whether your edge accepts 0-RTT

The OpenSSL one-liner:

openssl s_client -connect example.com:443 -tls1_3 -sess_out sess.pem -ign_eof <<< "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
openssl s_client -connect example.com:443 -tls1_3 -sess_in sess.pem -early_data req.txt

Where req.txt contains a raw HTTP request. If s_client prints Early data was accepted, your endpoint is replay-vulnerable.

The single-use registry: why "we deduplicate" isn't enough

RFC 8446 §8.2 describes a "single-use ticket" anti-replay mechanism. Sounds great. In practice it requires the server to maintain a synchronized state across every node that might terminate TLS for that session ticket. For a CDN with 200 PoPs, this is either:

Cloudflare published a 2017 paper on this topic. The honest summary: best-effort, with windows up to ~10 seconds in practice. For high-value endpoints, "we deduplicate" should be read as "we deduplicate most of the time."

The right defense: kill 0-RTT for state-changing endpoints

In nginx with BoringSSL:

server {
    ssl_early_data on;

    location /api/ {
        # Reject 0-RTT for the API
        if ($ssl_early_data) {
            return 425;  # Too Early
        }
        proxy_pass http://backend;
    }

    location / {
        # Allow 0-RTT for static assets — replay is harmless on idempotent reads of files
        proxy_pass http://backend;
    }
}

HTTP status 425 Too Early tells the client to retry with full handshake. Modern browsers handle this transparently. The application gets the latency win on static assets and the safety on writes.

UnveilScan and 0-RTT

Our tls_extended checker probes for 0-RTT acceptance and flags MEDIUM if the server accepts early data without an explicit 425 path discipline. If you've never turned it on, you'll never see this finding. If you turned it on for latency and never differentiated by route, it'll show up immediately.

Audit your TLS 1.3 posture

Free Basic scan covers TLS basics. Extended adds 0-RTT detection, OCSP stapling, SCT verification, weak cipher checks.

Run a scan