What is TLS 1.3 0-RTT, and why is it risky?
Show the answer
Say this first
0-RTT lets a client that has connected before send application data in its very first flight, encrypted with keys from a pre-shared key the server gave it in a session ticket. It saves a full round trip. The risk is replay: an attacker who captures the ClientHello and early data can send it again, and the server may process the request twice. Early data also lacks forward secrecy, because its keys come only from the ticket. So 0-RTT is only safe for idempotent requests, and many servers turn it off.
Go deeper
How it works
- In a previous connection, the server sent a NewSessionTicket.
- The new ClientHello carries
pre_shared_keyandearly_data, followed by application data encrypted with the early traffic key. - The server accepts by echoing early_data in EncryptedExtensions, or rejects it, in which case the client resends the data after the normal 1-RTT handshake.
Why replay is hard to stop
Within one server, single-use tickets or a cache of recent ClientHellos can catch duplicates. Across a cluster or many data centers, that state would have to be shared, so most deployments accept some replay window and limit what early data may do.
How applications protect themselves
- Only allow safe, idempotent requests in early data, such as GET without side effects.
- A server or CDN that forwards early data to a backend marks it with
Early-Data: 1; the backend can answer425 Too Earlyto make the client retry after the handshake (RFC 8470). - nginx has
ssl_early_data offby default.
They will ask next
Is session resumption without 0-RTT risky in the same way?
No. 1-RTT resumption is not replayable, because the server's fresh contribution is part of the keys. With the psk_dhe_ke mode it also does a new Diffie-Hellman exchange and keeps forward secrecy.
How would you test 0-RTT against a server?
Save a session with
openssl s_client -sess_out sess.pem, then reconnect with-sess_in sess.pem -early_data req.txtand check whether the output reports the early data as accepted.
Traps
- Saying TLS 1.3 handshakes are always 0-RTT. A first connection is 1-RTT; 0-RTT needs a prior ticket.