← Networking Mastery — Fundamentals to Principal

TLS & Network Security

TLS & Network Security

TLS is the reason you can enter a credit card number on a website without the coffee shop router owner reading it. It does three things simultaneously: it encrypts the data, proves who you’re talking to, and detects tampering. Every HTTPS request in the world goes through this.

Understanding TLS properly means understanding why each piece exists, not just what it does. The design makes complete sense once you know what attacks it’s defending against.


Why TLS Exists

The Three Pillars

Without TLS, HTTP is plaintext over a network. Anyone on the path — your router, your ISP, a rogue access point, a backbone provider — can do three things:

Read: intercept passwords, session tokens, API keys, private messages. Called eavesdropping.

Modify: inject ads into pages, change bank transfer amounts, replace downloads with malware. Called a man-in-the-middle (MITM) attack.

Impersonate: serve a fake bank.com page and collect credentials. Called spoofing.

TLS provides three corresponding defenses:

AttackTLS DefenseMechanism
EavesdroppingConfidentialitySymmetric encryption (AES-GCM)
TamperingIntegrityAuthenticated encryption (AEAD)
ImpersonationAuthenticationCertificate + PKI

You need all three. Encryption without authentication is pointless — you’re sending an encrypted envelope, but you don’t know who holds the other end. Authentication without encryption tells you who you’re talking to but lets everyone else read the conversation.

SSL vs TLS

They mean the same thing colloquially — always say TLS.

  • SSL 1.0, 2.0, 3.0: Netscape-era, all dead. SSLv3 deprecated RFC 7568 in 2015.
  • TLS 1.0 (1999), 1.1 (2006): both deprecated (RFC 8996, 2021). PCIDSS now bans them.
  • TLS 1.2 (2008): still widely deployed, secure when properly configured.
  • TLS 1.3 (2018): the standard. Use this.

The protocol is always TLS. “SSL certificate” is just what humans call an X.509 certificate. The name stuck.

ELI5: Calling it “SSL” today is like calling your smartphone a “cellular phone.” Technically not wrong about lineage, but the thing you’re actually using is completely different and the old name is retired.


TLS 1.2 Handshake

Full Handshake (2-RTT)

Before any application data flows, client and server must agree on: which cipher suite to use, exchange keys, and prove identities. TLS 1.2 takes two round trips to do this.

Client                                    Server
  │                                          │
  │── ClientHello ─────────────────────────→ │  RTT 1 starts
  │   (TLS version, random, cipher suites,   │
  │    session ID, extensions)               │
  │                                          │
  │ ←────────────────────────── ServerHello  │
  │ ←────────────────────────── Certificate  │
  │ ←────────────────── ServerKeyExchange    │  (ECDHE only)
  │ ←────────────────────── ServerHelloDone  │
  │                                          │  RTT 1 ends
  │── ClientKeyExchange ──────────────────→  │  RTT 2 starts
  │── ChangeCipherSpec ───────────────────→  │
  │── Finished (encrypted) ───────────────→  │
  │                                          │
  │ ←──────────────────── ChangeCipherSpec   │
  │ ←──────────────────────────── Finished   │
  │                                          │  RTT 2 ends
  │── [Application Data] ─────────────────→  │  Data flows here

Two full round trips before a single byte of application data. On a 100ms RTT link, that’s 200ms of overhead before the first request byte.

Cipher Suite Anatomy

The cipher suite is the negotiated agreement for the entire session. Example:

TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  │     │     │        │    │    │
  │     │     │        │    │    └── PRF/HMAC hash: SHA-256
  │     │     │        │    └─────── Mode: GCM (authenticated)
  │     │     │        └──────────── Key size: 128-bit AES
  │     │     └───────────────────── Certificate type: RSA
  │     └─────────────────────────── Key exchange: ECDHE
  └───────────────────────────────── Protocol: TLS

Each part is independently negotiated from what client and server both support.

RSA Key Exchange vs ECDHE

The original TLS key exchange used RSA directly: client encrypts a random value with the server’s public key, sends it, server decrypts it with private key — done. Simple, but fatally flawed: every session uses the server’s long-term private key to derive the session key. If an attacker records all traffic today and later obtains the private key, they can decrypt everything retroactively.

ECDHE (Elliptic Curve Diffie-Hellman Ephemeral) generates a fresh key pair per handshake. The private key is thrown away after the session. Compromise the server’s certificate private key tomorrow — you still cannot decrypt today’s sessions.

This property is called forward secrecy (or Perfect Forward Secrecy, PFS). ECDHE is mandatory in TLS 1.3. TLS 1.2 technically allows RSA key exchange; no good reason to use it.

ELI5: RSA key exchange is like using the same house key to lock every lockbox of secrets you’ve ever sent. Lose the key once and all your past lockboxes are open. ECDHE is like using a new padlock for each lockbox and destroying each key immediately after. Losing your house key tomorrow changes nothing about past lockboxes.

Session Resumption

Full handshake cost (CPU for key derivation, 2-RTT latency) is paid once. Session resumption saves the 1 extra RTT on subsequent connections.

Session IDs: Server assigns an ID. Client presents the ID on the next connection. Server looks up the session state and resumes. Works well for servers with persistent memory; breaks in multi-server setups without shared session cache.

Session tickets: Server encrypts session state with a server-side key, sends it to the client. Client presents the ticket on reconnect. Server decrypts and resumes. Stateless server side — scales better. Security depends on protecting the ticket encryption key.

Both reduce 2-RTT handshake to 1-RTT.


TLS 1.3 — The Modern Standard

TLS 1.2 accumulated 10 years of legacy options. TLS 1.3 deleted most of them.

What Was Removed

Removed FeatureWhy
RSA key exchangeNo forward secrecy
CBC ciphers (AES-CBC)Vulnerable to BEAST, Lucky13, POODLE
SHA-1 in signaturesCollision attacks practical since 2017
MD5Broken since 2004
CompressionCRIME attack
RenegotiationComplex, exploitable
Custom DH groupsLogjam attack, weak parameter choices

Everything that remained is secure. No configuration knobs to get wrong.

1-RTT Handshake

TLS 1.3 saves one round trip by sending key exchange data in the first flight:

Client                                    Server
  │                                          │
  │── ClientHello ─────────────────────────→ │  RTT 1 starts
  │   (TLS version, random, cipher suites,   │
  │    key_share: client's ECDHE public key) │
  │                                          │
  │ ←────────────────────────── ServerHello  │
  │   (server's ECDHE public key)            │
  │ ←──────────────────── {Certificate}      │  encrypted!
  │ ←──────────────── {CertificateVerify}    │
  │ ←──────────────────────────── {Finished} │
  │                                          │  RTT 1 ends
  │── {Finished} ─────────────────────────→  │
  │── [Application Data] ─────────────────→  │  Data flows!

The {} notation means encrypted. By the time the server sends its certificate, both sides already have enough information to derive the symmetric key. The certificate and Finished are encrypted in transit — a privacy improvement.

0-RTT Resumption (Early Data)

TLS 1.3 allows sending application data in the very first packet — before the handshake completes. The client uses a PSK (pre-shared key) from a previous session and sends early data immediately.

Client                              Server
  │── ClientHello + early data ──→  │  zero RTT before data!
  │ ←───────────────── ServerHello  │
  │ ←───────────── {Finished} ───── │
  │── {Finished} ────────────────→  │

The catch: 0-RTT data is replay-vulnerable. The server sees early data before verifying the client is live. An attacker recording the ClientHello packet can replay it to a different server or later. For idempotent reads (GET /user/profile) this might be acceptable. For non-idempotent operations (POST /transfer) it’s dangerous.

Best practice: only allow 0-RTT for safe, idempotent operations. Servers should implement replay protection (nonce tracking or time window checks).

TLS 1.3 Cipher Suites

Reduced from dozens in TLS 1.2 to exactly 5:

SuiteNotes
TLS_AES_128_GCM_SHA256Default. Fast, hardware-accelerated on modern CPUs
TLS_AES_256_GCM_SHA384Higher security margin, slightly slower
TLS_CHACHA20_POLY1305_SHA256Mobile/ARM without AES hardware
TLS_AES_128_CCM_SHA256IoT/constrained devices
TLS_AES_128_CCM_8_SHA256IoT, shorter auth tag

ChaCha20-Poly1305 exists because AES-GCM requires hardware acceleration (AES-NI) to be fast. Without it, AES-GCM is 3-5x slower than ChaCha20. On mobile devices or embedded ARM without AES-NI, ChaCha20 is faster and equally secure.

ELI5: TLS 1.3 is like a new kitchen where someone threw out every suspicious ingredient, weird gadget, and decade-old condiment. What’s left is a smaller set of tools that all definitely work and nobody has found a problem with yet.


Certificates and PKI

X.509 Certificate Contents

A certificate is a signed statement: “this public key belongs to this entity, signed by this authority.”

Key fields:

FieldExampleMeaning
SubjectCN=api.example.comWho owns this cert
IssuerCN=Let's Encrypt R3Who signed it
Public Key2048-bit RSA or P-256 ECDSAServer’s public key
Valid From/To2026-01-01 to 2026-04-01Validity window
SANsapi.example.com, www.example.comValid hostnames
Key UsagedigitalSignature, keyEnciphermentPermitted operations

SANs (Subject Alternative Names) are how you put multiple hostnames on one certificate. The CN field is legacy — browsers use SANs. Wildcard certs (*.example.com) cover one level only: api.example.com yes, v1.api.example.com no.

Certificate Chain

No browser ships with millions of website certificates. Instead, a hierarchy:

Root CA (self-signed, pre-installed in OS/browser)
    │  "I vouch for Let's Encrypt R3"
    ▼
Intermediate CA (cross-signed by root)
    │  "I vouch for api.example.com"
    ▼
End-entity certificate (your site's cert)

The server must send the full chain (end-entity + intermediates) during the TLS handshake. Missing intermediate is one of the most common certificate misconfiguration errors — your browser has the root pre-installed but not the intermediate, so it can’t verify the chain.

Validation: browser walks up the chain, verifying each certificate’s signature using the issuer’s public key. If every signature checks out and the chain reaches a trusted root, the certificate is valid.

Certificate Transparency (CT)

Starting from 2018, Chrome requires all new certificates to be logged in Certificate Transparency logs — public, append-only ledgers of every certificate issued.

Purpose: detect misissuance. If a CA accidentally (or maliciously) issues a certificate for google.com to someone who doesn’t own it, it shows up in the CT log and Google’s monitoring catches it. Before CT, misissuance could go undetected for months.

The server includes CT proof in the handshake (via TLS extension or OCSP stapling). Browsers verify the SCT (Signed Certificate Timestamp) was issued by a trusted log.

Let’s Encrypt and ACME

Let’s Encrypt issues free, 90-day DV (domain-validated) certificates via the ACME protocol (RFC 8555). Validation proves you control the domain, not that you are who you claim to be (that’s OV/EV certs).

ACME challenges:

  • HTTP-01: place a token at /.well-known/acme-challenge/<token>. ACME server fetches it over port 80.
  • DNS-01: add a TXT record _acme-challenge.example.com. Works for wildcard certs; requires DNS API access.
  • TLS-ALPN-01: serve a special certificate on port 443.

90-day lifetime forces automation. Tools like certbot, cert-manager (k8s), and Caddy handle this automatically.

Certificate Pinning

Pinning says: “I only trust this specific certificate (or public key), not the general PKI.” The client hardcodes the expected fingerprint and rejects any other cert, even a valid one from a trusted CA.

This sounds secure. It is a maintenance nightmare.

Common mistake: Implementing certificate pinning in a mobile app without an out-of-band update mechanism. When you rotate your certificate, all older app versions stop working. Many apps have been bricked by certificate rotation hitting pinned clients that couldn’t update. Use HPKP (deprecated) or domain pinning only with extreme care — and always pin a backup key.


Cipher Suites and Algorithms

Key Exchange: ECDHE

Diffie-Hellman lets two parties generate a shared secret over a public channel. Elliptic Curve DH uses smaller keys (256-bit) than classic DH (2048-bit RSA) with equivalent security.

The “Ephemeral” means: generate a fresh key pair for each connection, throw away the private key after. Forward secrecy comes from this — no stored private key to compromise later.

Named curves used: P-256 (NIST), P-384, X25519. X25519 is fast and has better implementation properties (no timing side channels by design). Use X25519 when possible.

Authentication: RSA, ECDSA, Ed25519

This is the certificate signature algorithm — proving the certificate is genuine.

AlgorithmKey SizeSpeedNotes
RSA-20482048 bitsOKWidely supported, larger certs
RSA-40964096 bitsSlowerMarginal security gain over 2048
ECDSA P-256256 bitsFastSmaller certs, same security as RSA-3072
Ed25519256 bitsFastestBest option, less widely supported

ECDSA gives smaller certificates (important for handshake size) and faster operations. For new deployments: ECDSA P-256. Ed25519 if you can guarantee modern client support.

Bulk Encryption: AEAD Ciphers

TLS 1.3 uses only AEAD (Authenticated Encryption with Associated Data) ciphers — encryption and integrity checking in one operation.

AES-128-GCM: AES in Galois/Counter Mode. Hardware-accelerated (AES-NI) on x86, ARM v8+. GCM provides authentication via GHASH. 128-bit key is secure for foreseeable future.

AES-256-GCM: Same but 256-bit key. Use when regulatory requirements demand it (FIPS, government), or for data that must remain confidential for 30+ years.

ChaCha20-Poly1305: Stream cipher + Poly1305 MAC. No hardware acceleration needed. Equal security to AES-GCM but faster in software. Designed by D.J. Bernstein with side-channel resistance as a primary goal.

ELI5: AES-GCM is like a car with a turbocharged engine — blazing fast, but needs special fuel (AES-NI hardware). ChaCha20 is a reliable diesel — not reliant on special hardware, runs fine everywhere, and is just as good for the job.

Forward Secrecy: Why It Matters

Consider: Governments and intelligence agencies collect encrypted traffic passively today, betting they can decrypt it in 10 years when quantum computers (or obtained private keys) make it possible. This is “harvest now, decrypt later.”

Forward secrecy means historical sessions cannot be decrypted even with the current private key. Each session’s ephemeral key pair exists briefly and is gone. Nothing to harvest later.

Every TLS 1.3 session has forward secrecy. TLS 1.2 only if ECDHE (not RSA) key exchange is negotiated.


mTLS — Mutual TLS

Standard TLS vs mTLS

Standard TLS authenticates only the server. The client verifies the server’s certificate. The server accepts any client.

Mutual TLS requires both sides to present certificates:

Client                                    Server
  │── ClientHello ─────────────────────────→ │
  │ ←────────────────────────── ServerHello  │
  │ ←────────────────────────── Certificate  │  server proves identity
  │ ←────────────────── CertificateRequest   │  server demands client cert
  │── Certificate (client cert) ───────────→ │  client proves identity
  │── CertificateVerify ───────────────────→ │
  │── Finished ───────────────────────────→  │
  │ ←──────────────────────────── Finished   │

The server validates the client’s certificate against its own trusted CA list. If the client can’t present a valid cert, the handshake fails.

Use Cases

Service-to-service in microservices: Internal services should not trust each other by default. With mTLS, payment-service can only call inventory-service if it holds a valid client certificate issued by your internal CA. No JWT, no API key — the identity is baked into the TLS layer.

Zero-trust networks: Treat every network segment as untrusted. Even internal traffic requires authentication. mTLS enforces this at the transport layer.

API authentication: Some financial APIs require client certificates instead of (or in addition to) API keys. Harder to steal than a credential string.

Service Mesh Implementation

Managing certificates for hundreds of services manually is unworkable. Service meshes (Istio, Linkerd) solve this:

  • Each service pod gets a short-lived certificate from the mesh’s internal CA (SPIFFE/SPIRE standard)
  • Sidecar proxy (Envoy, linkerd-proxy) handles TLS termination and mTLS to other sidecars
  • Application code talks plain HTTP locally; mTLS happens at the proxy layer
  • Certificate rotation is automatic

ELI5: Standard TLS is like showing ID at a bar — you (the customer) prove you’re old enough, but the bar doesn’t prove it’s a licensed establishment to you. mTLS is like when both the customer and the establishment check each other’s credentials — the bouncer checks your ID, and you check their operating license.

Common mistake: Running mTLS with long-lived certificates. If a service’s certificate is valid for 2 years and that service is compromised, the attacker can impersonate it for 2 years. Short-lived certificates (24h-7d) with automated rotation limit the blast radius.


Common TLS Issues and Debugging

Certificate Error Types

ErrorCauseFix
CERT_EXPIREDCertificate past Not After dateRenew cert (automate with ACME)
ERR_CERT_COMMON_NAME_INVALIDHostname not in CN or SANAdd hostname to SAN; check wildcard scope
UNABLE_TO_VERIFY_LEAF_SIGNATUREMissing intermediate CA in chainSend full chain from server
CERT_UNTRUSTEDCA not in trust storeInstall CA cert; for internal CA, distribute to clients
CERT_REVOKEDCertificate revoked via CRL/OCSPReissue a new cert

openssl s_client

The essential TLS debugging tool:

# Full TLS handshake and certificate info
openssl s_client -connect api.example.com:443

# Check specific TLS version
openssl s_client -connect api.example.com:443 -tls1_3

# Show full certificate chain
openssl s_client -connect api.example.com:443 -showcerts

# Check a specific SNI name (multiple vhosts on one IP)
openssl s_client -connect 1.2.3.4:443 -servername api.example.com

# Verify against custom CA
openssl s_client -connect api.example.com:443 -CAfile /path/to/ca.pem

Pay attention to: Verify return code: 0 (ok) at the bottom. Any non-zero is a problem. Also check Protocol: TLSv1.3, cipher negotiated, and certificate Not After date.

HSTS — HTTP Strict Transport Security

Once a browser sees Strict-Transport-Security: max-age=31536000; includeSubDomains, it will refuse plain HTTP to that domain for 1 year, even if you try to navigate to http://. Forces HTTPS even before the first redirect.

The preload list (includeSubDomains; preload) bakes the domain into browsers at compile time — HTTPS enforced on first visit, before any HTTP response has been received. Opt-in at hstspreload.org. Hard to undo.

OCSP Stapling

When a browser validates a certificate, it should check if the certificate has been revoked. Normally this means contacting the CA’s OCSP responder — a third-party request that adds latency and leaks which sites users visit to the CA.

OCSP stapling: the server periodically fetches its own OCSP response and attaches (“staples”) it to the TLS handshake. The client gets revocation status without contacting the CA directly. Faster, private, reliable.

Enable OCSP stapling in your web server config. Without it, browsers either skip the check (performance) or do the slow out-of-band check.

Mixed Content

An HTTPS page loading any HTTP resource (image, script, iframe) is “mixed content.” Browsers block active mixed content (scripts, iframes) outright. Passive mixed content (images) triggers a warning.

The correct fix is loading all resources over HTTPS. The shortcut is the Content-Security-Policy: upgrade-insecure-requests header which rewrites HTTP subresource URLs to HTTPS automatically.


TLS Attacks and Mitigations

Historical Attack Landscape

AttackYearExploitedStatus
BEAST2011CBC mode IV predictability in TLS 1.0Fixed by TLS 1.1+, mitigated by 1/n-1 split
CRIME2012gzip compression leaking secretsFixed by disabling compression
Lucky132013CBC padding oracle via timingFixed by constant-time padding, GCM
POODLE2014SSLv3 CBC padding oracleFixed by disabling SSLv3
Heartbleed2014OpenSSL buffer over-read in heartbeat extensionFixed in OpenSSL 1.0.1g
FREAK2015Forced downgrade to export-grade RSA (512-bit)Fixed by removing export ciphers
Logjam2015Weak DH parameters (512/1024-bit)Fixed by enforcing 2048-bit+ DH
BREACH2013gzip on HTTP body leaking secretsMitigated by CSRF tokens, length hiding
DROWN2016SSLv2 key reuse attacking TLS connectionsFixed by disabling SSLv2 entirely

The pattern: most attacks exploited legacy modes, weak parameters, or implementation bugs in specific code paths. TLS 1.3 deleted the legacy modes; Heartbleed-type bugs exist in implementation, not the protocol.

Downgrade Attacks

An attacker between client and server can interfere with the handshake to make both sides negotiate an older, weaker TLS version. The fix is downgrade protection:

  • TLS Fallback SCSV: a sentinel cipher suite value signaling “I’m trying a lower version because the higher one failed.” Server rejects it if it supports the higher version — prevents attacker-induced fallbacks.
  • TLS 1.3 encodes the version in a different field; the ClientHello.legacy_version is always TLS 1.2 for compatibility, but the actual negotiation is in extensions.

Always configure your server to reject TLS 1.0 and 1.1. If a client can’t do TLS 1.2, that’s their problem.

SNI and Privacy

Server Name Indication (SNI) is a TLS extension where the client sends the hostname it wants in the ClientHello — before encryption. Servers use this to serve the correct certificate when multiple sites share an IP.

SNI leaks the hostname to anyone on the network path. Even with HTTPS, your ISP (and any middlebox) knows which domain you connected to.

Encrypted Client Hello (ECH): new extension in TLS 1.3. Client encrypts the sensitive part of the ClientHello (including SNI) using the server’s public key (published in DNS via HTTPS records). The visible outer ClientHello uses only the CDN/shared hostname. Hostnames become private.

ECH is being deployed (Cloudflare, some browsers) but not universal yet.

ELI5: Standard TLS with SNI is like sending a letter in a sealed envelope — nobody can read the contents, but the address on the outside is visible to the postal worker. ECH puts the whole thing inside another sealed envelope addressed only to the sorting center, hiding the final destination too.

Perfect Forward Secrecy Under Attack

Why is PFS the single most important property for long-term security? Consider the threat model:

  1. Nation-state adversary stores all your encrypted traffic today.
  2. In 5 years they obtain your private key (hack, legal process, employee bribe).
  3. Without PFS (RSA key exchange): they decrypt everything from the past 5 years.
  4. With PFS (ECDHE): the ephemeral keys are gone. Stored traffic remains unreadable.

PFS doesn’t help against a real-time MITM (where they actively intercept and re-encrypt). But it protects against passive collection + future key compromise — which is the realistic large-scale threat.

Common mistake: Prioritizing cipher performance over PFS in server configuration. An RSA key exchange saves ~1ms on the handshake. The price is that all past traffic is recoverable if the private key is ever compromised. Not worth it.


TLS Debugging Cheatsheet

# Check certificate expiry
echo | openssl s_client -connect host:443 2>/dev/null \
  | openssl x509 -noout -dates

# Verify certificate chain is complete
openssl s_client -connect host:443 -showcerts 2>/dev/null \
  | grep -E 'subject|issuer'

# Test specific cipher suite
openssl s_client -connect host:443 -cipher ECDHE-RSA-AES128-GCM-SHA256

# Check OCSP stapling
openssl s_client -connect host:443 -status 2>/dev/null \
  | grep -A3 'OCSP response'

# Verify mTLS (client cert)
openssl s_client -connect host:443 \
  -cert client.crt -key client.key -CAfile ca.crt

Summary Reference

ConceptTLS 1.2TLS 1.3
Handshake RTTs21 (0-RTT possible)
Key exchangeECDHE or RSAECDHE only
Cipher suitesDozens (some weak)5 (all strong)
Forward secrecyOptional (ECDHE)Always
Certificate encryptedNoYes
CompressionOptional (vulnerable)Removed
RenegotiationSupportedRemoved
Downgrade protectionNeeds SCSVBuilt-in
LayerWhat it providesWhat it doesn’t
TLSEncrypted channel, server authenticationApplication-level authorization
CertificatesServer identity proofProof of trustworthiness (only DV for Let’s Encrypt)
mTLSMutual authenticationPrevents authorized clients from doing bad things
HSTSForces HTTPS on return visitsProtects first visit without preload
CT logsDetects misissuance after the factPrevents misissuance

The security model of TLS is: strong by default in TLS 1.3, configurable-into-weakness in TLS 1.2. Deploy TLS 1.3, disable TLS 1.0/1.1, use ECDHE-based cipher suites, enable OCSP stapling, automate certificate renewal, and log to CT. That covers 99% of real-world concerns.