Protocol Deep Dives

TLS 1.3: Faster, Simpler, More Secure

How TLS 1.3 reduced handshake latency (1-RTT/0-RTT), removed insecure ciphers, and simplified the protocol — comparison with TLS 1.2 and deployment guide.

TLS 1.3 vs TLS 1.2 — What Changed

TLS 1.3 (RFC 8446, 2018) is a near-complete rewrite of TLS 1.2. The goals were explicit: remove everything unsafe, reduce latency, and simplify the specification. The result is a cleaner, faster, and significantly more secure protocol.

Features Removed in TLS 1.3

Removed FeatureWhy It Was Dangerous
RSA key exchangeStatic RSA key — compromise of server key decrypts all past sessions (no forward secrecy)
DHE_EXPORT (weak Diffie-Hellman)FREAK, Logjam attacks — downgrade to 512-bit
CBC cipher suitesBEAST, Lucky13, POODLE attacks on CBC padding
RC4Biases in RC4 keystream — practical ciphertext recovery
MD5 and SHA-1 in signaturesCollision attacks (SHAttered, 2017)
CompressionCRIME attack — payload compression leaks secrets via size oracle
RenegotiationRenegotiation injection attacks (CVE-2009-3555)
Session resumption via session IDsServer-side state, less efficient than tickets

The list of removed features reads like a history of TLS vulnerabilities. TLS 1.3's security comes largely from *what it does not do*.

Forward Secrecy — Mandatory in TLS 1.3

Every TLS 1.3 handshake uses Ephemeral Diffie-Hellman (ECDHE or DHE) for key exchange. Fresh key material is generated per-connection and discarded afterward. An attacker who records encrypted traffic today and later compromises the server's private key gains nothing — the session keys are gone.

TLS 1.2 with RSA key exchange had no forward secrecy: the client encrypted the pre-master secret with the server's RSA public key. Compromise the private key and you decrypt every past session.

The 1-RTT Handshake

TLS 1.2 Handshake (2 RTTs)

Client                              Server
  │── ClientHello ─────────────────▶│  RTT 0 start
  │   (cipher suites, random)        │
  │◀── ServerHello ─────────────────│  RTT 0 end
  │◀── Certificate ─────────────────│
  │◀── ServerKeyExchange ───────────│
  │◀── ServerHelloDone ─────────────│
  │── ClientKeyExchange ────────────▶│  RTT 1 start
  │── ChangeCipherSpec ─────────────▶│
  │── Finished ─────────────────────▶│
  │◀── ChangeCipherSpec ────────────│  RTT 1 end
  │◀── Finished ────────────────────│
  │── HTTP Request ─────────────────▶│  RTT 2 start

TLS 1.2 required 2 full round-trips before the first application byte. On a 100ms RTT connection, that is 200ms of pure handshake latency.

TLS 1.3 Handshake (1 RTT)

Client                              Server
  │── ClientHello ─────────────────▶│  RTT 0 start
  │   (supported versions, key share)│
  │◀── ServerHello ─────────────────│
  │◀── {EncryptedExtensions} ───────│
  │◀── {Certificate} ───────────────│  Encrypted! (no MITM fingerprinting)
  │◀── {CertificateVerify} ─────────│
  │◀── {Finished} ──────────────────│  RTT 0 end
  │── {Finished} ───────────────────▶│  RTT 1 start
  │── [HTTP Request] ───────────────▶│  Sent immediately with Finished
  │◀── [HTTP Response] ─────────────│  RTT 1 end

In TLS 1.3, the client sends its key share in the ClientHello, the server responds with its key share in ServerHello, and both sides derive session keys immediately — allowing the server to encrypt the Certificate and Finished messages without waiting for another round-trip. The client sends its application data alongside the Finished message, saving one full RTT.

The Key Share in ClientHello

TLS 1.3 requires the client to guess which key exchange group the server supports and include a key share for that group in the ClientHello:

ClientHello extensions:
  supported_versions: [TLS 1.3, TLS 1.2]  ← negotiate version
  supported_groups:   [X25519, P-256, P-384]
  key_share:
    - group: X25519
      key_exchange: <32 bytes of ephemeral public key>

If the server supports X25519 (which almost all do), it uses the provided key share immediately. If not, it sends a HelloRetryRequest asking the client to resend with a different group — adding one RTT. Clients learn the server's preferred group and remember it for future connections.

0-RTT Early Data

How 0-RTT Works

After a successful TLS 1.3 connection, the server can issue a session ticket encrypted with a server-side key. On the next connection, the client presents this ticket and sends application data *before* receiving any server response:

Client                              Server
  │── ClientHello ─────────────────▶│
  │   + early_data extension         │
  │   + pre_shared_key (ticket)      │
  │── [HTTP GET /api/users] ─────────▶│  0-RTT data, no round-trip!
  │◀── ServerHello ─────────────────│
  │◀── {EncryptedExtensions} ───────│
  │◀── {Finished} ──────────────────│
  │◀── [HTTP 200 OK] ───────────────│  Response arrives immediately

The 0-RTT data is encrypted with the early traffic secret derived from the PSK — not from a fresh key exchange. This means 0-RTT data lacks forward secrecy relative to the PSK.

The Replay Attack Problem

0-RTT is vulnerable to replay attacks: an attacker who intercepts the 0-RTT ClientHello packet can resend it to the server, causing the server to process the same request twice. For a GET /prices this is harmless; for a POST /api/transfer this is catastrophic.

TLS 1.3 mitigations:

  • Single-use tickets: Server marks tickets as used (requires shared state across load-balanced servers — expensive)
  • Time-bound tickets: Include a ticket_age field; server rejects requests where the age is outside expected bounds
  • Application-level replay protection: Idempotency keys in request headers

The RFC recommendation: use 0-RTT only for idempotent requests (GET, HEAD) or requests with application-level replay protection.

Cipher Suites

TLS 1.3 Cipher Suite Simplification

TLS 1.2 had hundreds of cipher suites combining key exchange, authentication, bulk cipher, and MAC algorithms. This created a negotiation space so complex that misconfiguration was common. TLS 1.3 separates concerns:

  • Key exchange is always ECDHE or DHE (mandatory forward secrecy)
  • Authentication is always certificate-based or PSK
  • The cipher suite specifies only: bulk cipher + hash for HKDF

TLS 1.3 defines exactly five cipher suites:

TLS_AES_128_GCM_SHA256         ← Default, recommended for all TLS 1.3
TLS_AES_256_GCM_SHA384         ← High-security variant
TLS_CHACHA20_POLY1305_SHA256    ← Mobile-friendly (no AES-NI needed)
TLS_AES_128_CCM_SHA256         ← IoT/constrained devices
TLS_AES_128_CCM_8_SHA256       ← IoT with shorter authentication tag

All three main cipher suites use AEAD (Authenticated Encryption with Additional Data) — they simultaneously encrypt and authenticate the data, making padding oracle attacks (a major source of TLS 1.2 vulnerabilities) impossible by design.

ChaCha20-Poly1305 — For Devices Without AES Hardware

AES-GCM is extremely fast on processors with AES-NI instructions (every modern desktop/server CPU). But older mobile phones and IoT devices lack AES-NI — software AES is ~10x slower and vulnerable to timing side-channels.

ChaCha20-Poly1305 is designed to be fast in pure software without hardware instructions, while maintaining constant-time operation (no timing attacks). TLS 1.3 clients typically advertise both and let servers without AES-NI pick ChaCha20-Poly1305.

Deployment — Certificate Requirements and Middlebox Issues

Certificate Requirements

TLS 1.3 does not fundamentally change certificate requirements — RSA and ECDSA certificates both work. However, the industry has moved toward:

Recommended certificate configuration:
  Algorithm: ECDSA P-256 (smaller, faster than RSA-2048)
  Minimum RSA: 2048 bits (4096 bits for sensitive applications)
  Key usage:  TLS Web Server Authentication (1.3.6.1.5.5.7.3.1)
  SANs:       Include both apex and www (plus all relevant subdomains)
  Validity:   90 days (Let's Encrypt default) to 398 days (CA/Browser maximum)
# Check TLS version and cipher suite negotiated:
openssl s_client -connect example.com:443 -tls1_3 2>&1 | grep -E 'Protocol|Cipher'
# Protocol  : TLSv1.3
# Cipher    : TLS_AES_256_GCM_SHA384

# Using curl:
curl -v --tlsv1.3 https://example.com 2>&1 | grep TLS

Downgrade Protection

TLS 1.3 protects against downgrade attacks where a MITM strips TLS 1.3 support from the ClientHello to force a weaker TLS 1.2 negotiation. The server embeds a downgrade sentinel in the ServerRandom field:

If negotiated TLS 1.2 (but client offered 1.3):
  ServerRandom[24:32] = 44 4F 57 4E 47 52 44 01  ← 'DOWNGRD' + 0x01

TLS 1.3 clients check for this sentinel and abort if present —
a legitimate server that actually supports TLS 1.3 would never set it.

Middlebox Compatibility

Many corporate TLS inspection boxes ("SSL bump" proxies) broke on initial TLS 1.3 because they expected TLS 1.2 handshake patterns. TLS 1.3 includes a legacy session ID field and a legacy_session_id_echo in ServerHello that mimic TLS 1.2 patterns, helping TLS 1.3 pass through buggy middleboxes.

The key_share extension is placed after the standard extensions in ClientHello for the same reason — middleboxes that forward up to a fixed extension offset see familiar TLS 1.2 structure before the new TLS 1.3 fields.

# Nginx TLS configuration — TLS 1.3 only:
server {
    listen 443 ssl;
    ssl_protocols TLSv1.3;         # TLS 1.3 only (drop 1.2 for new deployments)
    ssl_certificate     /etc/ssl/server.crt;
    ssl_certificate_key /etc/ssl/server.key;

    # Enable 0-RTT early data (use only for idempotent endpoints):
    ssl_early_data on;
    add_header Early-Data $tls1_3_early_data;  # Log if request used 0-RTT
}

When to Still Support TLS 1.2

Pure TLS 1.3 deployments are appropriate for:

  • Public APIs with modern client requirements
  • Internal microservices (you control the clients)
  • New applications with no legacy client base

Continue supporting TLS 1.2 alongside TLS 1.3 for:

  • Consumer-facing services (some users on old Android/iOS)
  • Enterprise clients with corporate proxy requirements
  • Any service where a 0.1% client compatibility loss is unacceptable

The practical recommendation: configure ssl_protocols TLSv1.2 TLSv1.3 and let clients negotiate the best version they support. Remove TLS 1.2 only when telemetry confirms no clients use it.

관련 프로토콜

관련 용어

더 보기: Protocol Deep Dives