TLS quietly underpins almost everything: HTTPS, email transport, VPNs, database connections, service-to-service traffic in modern microservices. TLS 1.3 (RFC 8446, finalised in 2018) is the version you should be running everywhere by now — it is faster, dramatically simpler, and removes a long list of historically dangerous options. Understanding what the handshake actually does, what changed from 1.2, and how mutual TLS (mTLS) fits in is genuinely useful whether you are deploying a web server, debugging a broken integration, or designing a zero-trust network.
Why TLS 1.3 matters
TLS 1.2 accumulated nearly a decade of footguns: RSA key exchange (no forward secrecy), CBC ciphers vulnerable to padding oracles, RC4, SHA-1 in certificates, compression (CRIME), renegotiation issues, and dozens of cipher suite permutations to misconfigure. Attacks like BEAST, CRIME, BREACH, Lucky13, POODLE, FREAK, Logjam, and ROBOT all exploited corners of TLS 1.2 or earlier. TLS 1.3 was a deliberate clean-sheet design: drop everything dangerous, mandate forward secrecy, modernise the handshake, and make misconfiguration much harder. The cipher suite list shrunk from dozens to just five AEAD options. Most browsers, load balancers, and modern libraries now default to TLS 1.3 when both sides support it.
The TLS 1.3 handshake, step by step
A standard handshake takes one round trip (1-RTT) before encrypted application data flows. The basic flow:
supported_groups), signature algorithms, and a key share: one or more ephemeral public keys for the curves it expects the server to choose (typically X25519 or P-256). It also sends extensions like SNI (the hostname) and ALPN (the application protocol).A few important properties fall out of this design:
- Forward secrecy is mandatory – Every session uses fresh ephemeral ECDHE keys. Compromising the server's long-term private key later does not let an attacker decrypt past sessions captured on the wire.
- The certificate is encrypted – Passive observers see only the SNI (and even that can be encrypted with ECH in modern browsers and CDNs).
- 0-RTT (early data) – Resumed sessions can send application data in the very first flight using a pre-shared key from a previous session. It is faster, but replayable by design; only use it for idempotent requests.
Cipher suites and key exchange
TLS 1.3 defines just five cipher suites, all AEAD (Authenticated Encryption with Associated Data):
TLSAES128GCMSHA256TLSAES256GCMSHA384TLSCHACHA20POLY1305_SHA256TLSAES128CCMSHA256TLSAES128CCM8_SHA256(avoid in general use; shorter tag)
Key exchange is always (EC)DHE — RSA key transport is gone. Sensible curves are X25519 (fast, constant-time, widely supported) and P-256. Signature algorithms used by certificates have also been pruned: ECDSA with P-256/P-384 or RSA-PSS are the modern defaults; SHA-1 is dead.
A reasonable modern policy for a public HTTPS service: TLS 1.3 enabled, TLS 1.2 enabled only with AEAD suites and ECDHE for backwards compatibility, everything older disabled. Test with testssl.sh, SSL Labs, or nmap --script ssl-enum-ciphers.
Mutual TLS (mTLS)
Standard TLS authenticates the server to the client. mTLS authenticates the client to the server as well, using a client certificate. It is the workhorse of zero-trust service-to-service authentication in modern infrastructure: service meshes (Istio, Linkerd), VPN replacements (BeyondCorp-style), IoT device authentication, and high-assurance B2B APIs.
The mechanics are an extension of the normal handshake: the server sends a CertificateRequest, the client responds with its certificate and a CertificateVerify signed with its private key, proving possession. Both sides validate the other's chain against a trusted CA.
Practical considerations for running mTLS at scale:
- Have a real PKI – Issuing client certs by hand does not scale past a handful. Use a private CA (HashiCorp Vault, AWS Private CA, smallstep, or a service mesh CA) that automates issuance and rotation.
- Short-lived certificates – Hours or days, not years. This sidesteps most revocation problems; if a credential is stolen it expires before it can be abused much.
- Automate rotation – Workloads should renew their own certificates well before expiry. SPIFFE/SPIRE is a common standard for this.
- Revocation strategy – If you must use long-lived certs, OCSP stapling and short-lifetime CRLs are workable, but short-lived certs are simpler.
- Mind the trust store – Each service should trust only the issuing CA, not the public web PKI. Mixing the two creates accidental trust paths.
Operational pitfalls
A few patterns cause most of the TLS-related incidents in practice. Certificate expiry is still the largest self-inflicted outage source; automate renewal (ACME / Let's Encrypt for public, internal automation for private) and alert at multiple thresholds (60/30/7 days). Mismatched protocol or cipher requirements between client and server cause silent fallback to older versions when not pinned; modern services should refuse anything below TLS 1.2. SNI is sent in plaintext by default — if you need to hide the hostname, look at Encrypted Client Hello (ECH), now supported by Cloudflare and recent browsers. TLS termination at a load balancer is fine for the public edge, but treat internal hops as untrusted: use TLS or mTLS end-to-end where the data warrants it, not just at the perimeter. Finally, key material lives somewhere; protect private keys with HSMs or cloud KMS for high-value certificates, restrict file permissions on the rest, and never commit them to source control. TLS is one of the few areas in security where defaults have genuinely got better — make sure your stack actually uses them.