TLS 1.3 (RFC 8446, August 2018) isn’t an incremental update to TLS 1.2. It’s a redesign. Entire categories of insecure cryptographic options were removed — not deprecated, removed. The handshake was reduced from 2 round trips to 1. The server certificate is now encrypted (invisible to network observers). And the cipher suite list went from hundreds of combinations to exactly 5 — all secure.
If you’re still running TLS 1.2 only, you’re not insecure (TLS 1.2 with proper configuration is fine). But you’re leaving performance on the table and maintaining a larger attack surface than necessary. Here’s what changed, what it means operationally, and how to migrate safely.
The Key Differences
| Aspect | TLS 1.2 | TLS 1.3 |
|---|---|---|
| Handshake latency | 2-RTT (two round trips) | 1-RTT (one round trip) |
| 0-RTT resumption | Not available | Available (with replay caveats) |
| Forward secrecy | Optional (depends on cipher suite) | Mandatory (ECDHE always) |
| RSA key exchange | Allowed (no forward secrecy) | Removed entirely |
| CBC ciphers | Allowed (padding oracle risk) | Removed entirely |
| RC4, 3DES, DES | Allowed (if configured) | Removed entirely |
| SHA-1 signatures | Allowed | Removed |
| Certificate encryption | Plaintext (visible to observers) | Encrypted (after ServerHello) |
| Cipher suites | Hundreds of combinations | 5 total (all AEAD) |
| Compression | Allowed (CRIME attack) | Removed |
| Renegotiation | Allowed (complex, attack-prone) | Removed |
| Session tickets | Optional | Replaced with PSK-based resumption |
Handshake Comparison
TLS 1.2 (2 Round Trips)
Client Server
| |
|--- ClientHello ------------------>| (cipher suites, random)
| |
|<-- ServerHello -------------------| (selected cipher, random)
|<-- Certificate -------------------| (server cert — PLAINTEXT!)
|<-- ServerKeyExchange -------------| (ECDHE params, if using ECDHE)
|<-- ServerHelloDone ---------------|
| |
|--- ClientKeyExchange ------------>| (client's ECDHE public value)
|--- ChangeCipherSpec ------------->|
|--- Finished --------------------->|
| |
|<-- ChangeCipherSpec --------------|
|<-- Finished ----------------------|
| |
|=== Application Data ==============| (encrypted)
Total: 2 round trips before data flows. On a 100ms latency connection, that’s 200ms of handshake overhead.
TLS 1.3 (1 Round Trip)
Client Server
| |
|--- ClientHello ------------------>| (cipher suites, key shares!)
| |
|<-- ServerHello -------------------| (selected cipher, key share)
|<-- {Certificate} ----------------| (ENCRYPTED!)
|<-- {CertificateVerify} ----------| (ENCRYPTED!)
|<-- {Finished} -------------------| (ENCRYPTED!)
| |
|--- {Finished} ------------------->|
| |
|=== Application Data ==============| (encrypted)
Total: 1 round trip. The client sends key shares speculatively in the first message. The server can compute the shared secret immediately. Everything after ServerHello is encrypted.
Performance impact: On a 100ms connection, TLS 1.3 saves 100ms per new connection. At scale (thousands of new connections/second), this is measurable in both latency and server CPU.
What Was Removed (And Why)
RSA Key Exchange (No Forward Secrecy)
In TLS 1.2, one mode encrypts the pre-master secret with the server’s RSA public key. If the server’s private key is later compromised (or subpoenaed), every recorded session can be decrypted retroactively.
TLS 1.3 mandates ephemeral key exchange (ECDHE). Each session uses unique keys that are discarded after use. Past sessions remain encrypted even if the server key is compromised.
Action: Disable TLS_RSA_WITH_* cipher suites in TLS 1.2 immediately. They provide no forward secrecy.
CBC Mode Ciphers (Padding Oracle Attacks)
CBC ciphers in TLS 1.2 are vulnerable to padding oracle attacks (BEAST, Lucky13, POODLE). These attacks exploit timing differences in how servers handle invalid padding to decrypt data byte-by-byte.
TLS 1.3 only allows AEAD ciphers (AES-GCM, ChaCha20-Poly1305) which have no padding and provide authenticated encryption.
Action: Disable all *_CBC_* cipher suites in TLS 1.2. Use only GCM or ChaCha20.
Compression (CRIME Attack)
TLS-level compression leaks information about plaintext through compressed size differences. The CRIME attack exploits this to steal session cookies.
TLS 1.3 removes compression entirely.
Action: Ensure ssl_comp_off (or equivalent) in TLS 1.2 configuration.
TLS 1.3 Cipher Suites (All 5)
TLS_AES_256_GCM_SHA384 (AES-256 + GCM + SHA-384 for HKDF)
TLS_AES_128_GCM_SHA256 (AES-128 + GCM + SHA-256 for HKDF)
TLS_CHACHA20_POLY1305_SHA256 (ChaCha20 + Poly1305 + SHA-256)
TLS_AES_128_CCM_SHA256 (AES-128 + CCM — for constrained devices)
TLS_AES_128_CCM_8_SHA256 (AES-128 + CCM with 8-byte tag — IoT)
That’s it. No RSA key exchange to choose. No CBC mode. No weak options. You literally cannot configure an insecure TLS 1.3 connection.
Configuration
Nginx (TLS 1.2 + 1.3, Recommended)
ssl_protocols TLSv1.2 TLSv1.3;
# TLS 1.3 cipher suites (OpenSSL 1.1.1+)
ssl_conf_command Ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256;
# TLS 1.2 cipher suites (only ECDHE + AEAD)
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
ssl_prefer_server_ciphers off; # Let client choose in TLS 1.3
ssl_ecdh_curve X25519:P-256:P-384;
# 0-RTT (optional — read security implications first)
ssl_early_data on;
Apache
SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLCipherSuite TLSv1.3 TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305
SSLHonorCipherOrder off
AWS ALB
Security Policy: ELBSecurityPolicy-TLS13-1-2-2021-06
# Supports TLS 1.3 + strong TLS 1.2 suites
# Older policies (2016-08, etc.) don't include TLS 1.3
Migration: Enabling TLS 1.3
Step 1: Verify Server Support
# Check OpenSSL version (need 1.1.1+ for TLS 1.3)
openssl version
# OpenSSL 3.2.1 — good
# Test TLS 1.3 negotiation
openssl s_client -connect yourserver.com:443 -tls1_3
# If successful: "Protocol : TLSv1.3"
Step 2: Enable TLS 1.3 Alongside 1.2
Don’t disable TLS 1.2 yet. Enable both:
ssl_protocols TLSv1.2 TLSv1.3;
Step 3: Monitor Client Distribution
Check what percentage of clients negotiate TLS 1.3 vs 1.2:
# From access logs or monitoring
# Most modern browsers (Chrome 70+, Firefox 63+, Safari 12.1+) use TLS 1.3
# Legacy clients (Java 8, old Android, Windows 7) use TLS 1.2
Step 4: Test Through Your Full Network Path
TLS 1.3 can break middleboxes (firewalls, DLP, IDS) that inspect TLS traffic:
- The encrypted certificate in TLS 1.3 prevents middleboxes from seeing which site you’re connecting to
- Some middleboxes corrupt the handshake trying to parse TLS 1.3 messages
- Test from behind your corporate proxy, WAF, and any network inspection devices
Step 5: Disable TLS 1.2 (Optional, Future)
Only after confirming all clients support TLS 1.3:
ssl_protocols TLSv1.3; # TLS 1.3 only — no fallback
Most organizations will keep TLS 1.2 enabled for years (legacy client support). This is fine — TLS 1.2 with ECDHE + AEAD is secure.
0-RTT Resumption: The Trade-Off
TLS 1.3 offers 0-RTT (zero round trip) resumption — returning clients can send encrypted data in the very first message. Zero latency overhead.
The catch: 0-RTT data has no replay protection at the TLS layer. An attacker who captures the first flight can replay it.
Safe for: GET requests, idempotent operations, static content Dangerous for: POST requests, payment transactions, any state-changing operation
# Enable 0-RTT (Nginx)
ssl_early_data on;
# Application MUST check the Early-Data header:
# If Early-Data: 1 → request arrived via 0-RTT → reject non-idempotent operations
proxy_set_header Early-Data $ssl_early_data;
Recommendation: Enable 0-RTT only if your application checks the Early-Data header and rejects non-idempotent requests in early data. Otherwise, leave it disabled.
What Breaks During Migration
Middlebox Interference
Enterprise firewalls and DLP appliances that perform TLS inspection often break TLS 1.3:
- They can’t read the encrypted certificate (used for filtering decisions)
- They may corrupt the handshake trying to parse unknown extensions
- Symptoms: intermittent connection failures, only from behind corporate network
Fix: Update middlebox firmware. Most vendors (Palo Alto, Fortinet, Zscaler) have TLS 1.3 support in recent versions.
Java 8 Clients
Java 8 does NOT support TLS 1.3 (without backport patches). If you have Java 8 clients connecting to your services and you disable TLS 1.2, they can’t connect.
Fix: Keep TLS 1.2 enabled as fallback, or upgrade Java clients to 11+.
Older Load Balancers
Some older F5 BIG-IP and Citrix ADC firmware versions don’t support TLS 1.3. They’ll negotiate TLS 1.2 (fine) but can’t offer TLS 1.3 to clients that prefer it.
Fix: Update firmware or replace hardware.
FAQ
Q: Is TLS 1.2 insecure? A: No — TLS 1.2 with proper configuration (ECDHE + AEAD ciphers, no CBC, no RSA key exchange) is secure. TLS 1.3 is better (faster, simpler, smaller attack surface), but properly-configured TLS 1.2 is not a vulnerability.
Q: Should I disable TLS 1.2? A: Not yet for most organizations. Too many legacy clients still need it. Enable TLS 1.3 (for performance and security benefits with modern clients) while keeping TLS 1.2 as fallback. Disable TLS 1.2 only when your client population fully supports 1.3.
Q: Does TLS 1.3 affect certificate management? A: No. The same certificates work with both TLS 1.2 and 1.3. No certificate changes needed. The difference is in the handshake protocol, not the certificate format.
Q: What about QUIC/HTTP3? A: QUIC (HTTP/3) uses TLS 1.3 internally for its handshake — but over UDP instead of TCP. If you support HTTP/3, you’re already using TLS 1.3 semantics. The certificate management is identical.
Q: How do I verify TLS 1.3 is working?
# Test from command line
openssl s_client -connect example.com:443 -tls1_3
# Look for: "Protocol : TLSv1.3" and "Cipher : TLS_AES_256_GCM_SHA384"
# Or use curl
curl -v --tls13-ciphers TLS_AES_256_GCM_SHA384 https://example.com 2>&1 | grep "SSL connection"