
So far, our network stack stands four layers tall: MAC addresses find the next hop within a LAN, IP addresses provide global addressing, routing tables guide hop-by-hop forwarding, and TCP ensures data arrives in order, without loss. Then HTTP defines the request-response conversation format on top of all this.
But one critical piece is missing: security.
TCP guarantees data arrives "intact" — but "intact" here means byte-for-byte complete, nothing more. TCP can't prevent those bytes from being eavesdropped on. It can't detect whether they've been tampered with mid-transit (MITM attack). And it certainly can't verify that the party you're talking to is who they claim to be (spoofing). When your TCP segments travel through a transatlantic submarine cable, anyone with access to the optical signal can, in theory, read their contents.
TLS (Transport Layer Security) fills this gap. It sits above TCP and below HTTP, adding three guarantees to the reliable-but-insecure TCP pipe: encryption, integrity verification, and authentication.
Understand TCP's three-way handshake and byte-stream transport model
Understand HTTP's request-response model
You're at a coffee shop on their public Wi-Fi, logging into GitHub. You type your password and press Enter.
Without HTTPS, your input password=MySecret123 would be placed into the HTTP request body in plaintext, then segmented by TCP, wrapped into IP packets, and broadcast over Wi-Fi radio waves. Anyone in that coffee shop running Wireshark could capture those packets and read your password.
This is why you need TLS. But what exactly does TLS do? Why does a little green padlock appear in your browser's address bar? What actually happens behind that lock?
Let's break it down from the protocol stack perspective.
HTTP (Application Layer — defines "what to say")
─────────────────
TLS (Security Layer — encryption, authentication, integrity) ← This article's focus
─────────────────
TCP (Transport Layer — ensures reliable delivery)
─────────────────
IP (Network Layer — addressing and routing)TLS sitting between HTTP and TCP is a subtle but powerful design choice. From TCP's perspective, TLS produces encrypted data that's just ordinary "application-layer payload" — TCP doesn't care what those bytes are; it only cares about reliable delivery. From HTTP's perspective, TLS is transparent — HTTP sends GET /index.html HTTP/1.1 in plaintext as usual; TLS encrypts it automatically underneath, and HTTP doesn't even know it's traveling over an encrypted channel.
This "middle layer" design lets TLS protect any TCP-based protocol — not just HTTP (HTTPS), but also SMTP (SMTPS), FTP (FTPS), IMAP, and more.
Encryption: Preventing Eavesdropping
TLS uses symmetric encryption algorithms (like AES-128-GCM) to encrypt communication content. "Symmetric" here means both server and client use the same key — consistent with TCP's communication model: once a connection is established, both sides send and receive data bidirectionally over a single channel.
Integrity: Preventing Tampering
Every TLS record carries a Message Authentication Code (MAC), allowing the receiver to verify that the data hasn't been modified in transit. This serves a similar purpose to TCP's checksum, but TCP's checksum only detects random bit errors (like electromagnetic interference causing bit flips) — it can't prevent an attacker from deliberately modifying data and recalculating the checksum. TLS's MAC is based on cryptographic keys; without the key, an attacker cannot forge a valid MAC.
Authentication: Preventing Impersonation
The server (and optionally the client) proves its identity using digital certificates. Certificates are signed by a CA (Certificate Authority), forming a chain of trust — you trust the CA (because your OS and browser ship with the CA's root certificate pre-installed), the CA vouches for the website's identity, so you trust the website.
TLS 1.2 uses a classic 2-RTT handshake. But TCP has already consumed 1.5 RTT for its three-way handshake before TLS even begins. So TCP + TLS 1.2 costs a total of roughly 3.5 RTT — only after which the first HTTP byte can be transmitted.
Timeline (from the client's perspective):
0 RTT TCP handshake complete (1.5 RTT)
0.5 RTT Send ClientHello
1 RTT Receive ServerHello, Certificate, ServerKeyExchange, ServerHelloDone
→ Send ClientKeyExchange, ChangeCipherSpec, Finished
2 RTT Receive ChangeCipherSpec, Finished
→ ⭐ TLS handshake complete, HTTP requests can beginHere's each step in the handshake:
The client sends:
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256Decoding a cipher suite name:
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
│ │ │ │
│ │ │ └── Hash algorithm: SHA-256 (integrity check)
│ │ └── Symmetric encryption: AES-128-GCM (encrypting traffic)
│ └── Authentication: RSA signatures (certificate verification)
└── Key exchange: ECDHE (ephemeral elliptic-curve keys for forward secrecy)The server picks the best mutually supported cipher suite from the client's list and sends:
The server sends an X.509 certificate containing:
Client-side verification:
This verification builds on a chain of trust — you trust the root CAs pre-installed in your OS (like DigiCert, Let's Encrypt); the root CA signed an intermediate CA; the intermediate CA signed the website's certificate.
(Only required when using ephemeral key algorithms like ECDHE/DHE)
The server sends elliptic curve parameters (e.g., secp256r1) and an ephemeral public key. The word "ephemeral" is crucial — this isn't the long-term public key stored in the certificate; it's generated fresh for this session. This means even if the server's long-term private key is compromised in the future, past sessions cannot be decrypted. This is Forward Secrecy (PFS).
These parameters carry a digital signature to prevent MITM tampering.
This is the most critical computation in the handshake. The client:
Upon receiving it, the server uses its ephemeral private key + the client's ephemeral public key → computes the same Pre-Master Secret. This is the mathematical magic of ECDHE — each side uses its own private key and the other's public key, arriving at the same shared secret, which was never transmitted over the network.
With the Pre-Master Secret, both sides independently perform key derivation:
Master Secret = PRF(
Pre-Master Secret,
"master secret",
Client Random + Server Random
)
Key Block = PRF(
Master Secret,
"key expansion",
Server Random + Client Random
)The PRF (Pseudo-Random Function) is TLS 1.2's key derivation function, based on HMAC-SHA256. Its job is to process the "raw materials" (Pre-Master Secret + randoms) into multiple concrete session keys.
The Key Block is then carved into six key segments:
| Key Segment | Purpose |
|---|---|
| Client MAC Key | Verifies client message integrity |
| Server MAC Key | Verifies server message integrity |
| Client Encryption Key | Encrypts client-to-server data (AES key) |
| Server Encryption Key | Encrypts server-to-client data (AES key) |
| Client IV | Initialization vector for client encryption (needed for some cipher modes) |
| Server IV | Initialization vector for server encryption |
Why six different keys? Security isolation: encryption keys are separate from integrity keys; client and server directions are independent. Even if an attacker somehow guesses one key, they can't decrypt the entire conversation.
Both sides send a ChangeCipherSpec message ("everything from now on will be encrypted"), followed by a Finished message — the first message encrypted with the negotiated cipher, containing a hash of the entire handshake transcript. The other side decrypts and verifies the hash; if it matches, no MITM tampered with any handshake parameters.
At this point, the TLS 1.2 handshake is complete. All subsequent HTTP traffic is encrypted with AES-128-GCM, wrapped in TLS Records, and transmitted as TCP payload.
A common question: why can't TLS work like connecting to Wi-Fi — just enter a password and start encrypting?
Because TLS cannot rely on any "pre-shared secret." The first time you open github.com, your browser and the server have nothing in common except a routable IP. The TLS handshake must establish a secure channel from scratch, over a completely insecure medium. It must simultaneously:
Two round trips (2-RTT) is the minimum number required to accomplish these four tasks without any prior shared state.
TLS 1.3 (August 2018, RFC 8446) is a major simplification. Its core philosophy, in one sentence: cut out every insecure option, and let what remains finish in a single round.
1. Deprecated insecure algorithms
TLS 1.3 outright removed RSA key exchange, static DH, CBC-mode encryption, SHA-1 hashing, and other algorithms no longer considered secure. Client and server no longer need to negotiate "which algorithm to use" — the viable set is drastically reduced, and the default behavior is secure by design.
2. Handshake consolidated to 1-RTT
TLS 1.3 merges the ClientHello and the second-round messages into a single exchange:
TLS 1.2 Handshake (2-RTT):
Client → ServerHello
Server → ServerHello + Certificate + ServerKeyExchange + ServerHelloDone
Client → ClientKeyExchange + ChangeCipherSpec + Finished
Server → ChangeCipherSpec + Finished
TLS 1.3 Handshake (1-RTT):
Client → ClientHello + key share
Server → ServerHello + key share + Certificate + Finished
Client → Finished ← HTTP request can piggyback right hereThe key change: the server includes the key share, certificate, and Finished in its first reply. The client can immediately compute the session key upon receipt and start sending HTTP requests right after its own Finished message.
3. 0-RTT session resumption
For previously connected servers, TLS 1.3 allows the client to include encrypted application data alongside its first ClientHello:
0-RTT Resumption:
Client → ClientHello + key share + ★ Early Data (HTTP request) ★This works via PSK (Pre-Shared Key) — a key negotiated during a previous session is cached and reused. However, 0-RTT data lacks forward secrecy — if an attacker records the 0-RTT data and the server's long-term key is later compromised, that data can be decrypted. For this reason, 0-RTT is only suitable for idempotent requests (GETs, non-sensitive queries).
Laying TCP and TLS costs side by side:
TCP + TLS 1.2 (total ~3.5 RTT):
TCP three-way handshake (1.5 RTT) → TLS handshake (2 RTT)
TCP + TLS 1.3 (total ~2.5 RTT):
TCP three-way handshake (1.5 RTT) → TLS handshake (1 RTT)
TCP + TLS 1.3 + 0-RTT (total ~1.5 RTT):
TCP three-way handshake (1.5 RTT) + TLS handshake + ★ HTTP request in parallel ★This is also why HTTP/3 chose UDP-based QUIC — QUIC fuses the "transport layer" (TCP-like reliable delivery) and the "security layer" (TLS 1.3-like encryption) into a single protocol, capable of completing "connection establishment + encryption negotiation + data transmission" within 0-RTT. No amount of optimization can push TCP + TLS below the 1.5 RTT floor.
After the handshake, TLS enters the data transfer phase. The Record Layer is responsible for "packaging" HTTP plaintext into encrypted TLS Records, which are then handed to TCP for transmission.
The structure of a TLS Record:
+-------------------+
| ContentType (1B) | 23 = Application Data (i.e., HTTP data)
+-------------------+
| Version (2B) | TLS 1.2 = 0x0303
+-------------------+
| Length (2B) | Length of encrypted data
+-------------------+
| Encrypted Payload | Encrypted HTTP data + MAC + possible padding
+-------------------+From TCP's perspective, one TLS Record may map to multiple TCP segments, or multiple TLS Records may fit into a single TCP segment. There is no alignment relationship between Record Layer boundaries and TCP segment boundaries — the same observation we made about HTTP/2 frames and TLS Records.
The critical point: TCP knows nothing about the contents of TLS Records — it only sees a byte stream identified by sequence numbers. Encryption, decryption, and MAC verification are all handled by the TLS layer, completely transparent to TCP.
https://github.com, with the filter set to ssl. Observe the cipher suite negotiation in the ClientHello and ServerHello messages.openssl s_client -connect github.com:443 -tls1_2 and -tls1_3 to test TLS 1.2 and 1.3 respectively, and observe the differences in handshake steps.This article traced how TLS establishes secure communication atop insecure TCP. You now understand the cryptographic handshake, certificate validation, and the full derivation chain from Pre-Master Secret to session keys. But before any HTTPS connection, there's an invisible prerequisite step — translating the domain name into an IP address.
When you type github.com and press Enter, your browser's very first action is not a TCP handshake, not a TLS handshake, not an HTTP request — it's a DNS query. DNS is the internet's phonebook and the first link in every HTTP request chain.
The next article dives into DNS — the hierarchical domain namespace, recursive vs. iterative queries, record types (A/AAAA/CNAME/MX/TXT), caching and TTL, and modern DNS privacy with DoH/DoT.