Networking Interview Prep
Application Protocols

TLS/SSL

Encryption in Transit

LinkedIn Hook

You open your banking app. In 200 milliseconds, before your first byte of real data crosses the wire, your phone and the server have:

  • Agreed on a cipher suite from dozens of options
  • Exchanged cryptographic key material without ever sending the key itself
  • Verified the server's identity using a global chain of trust
  • Derived a fresh symmetric key that has never existed before and will never exist again

That is TLS 1.3. One round trip. Zero prior shared secrets.

Most engineers say "SSL" and mean TLS. Most engineers think TLS is just HTTPS. And most engineers could not explain why TLS uses two encryption algorithms instead of one.

This lesson covers all of it:

  • Why SSL is dead (and was murdered by a bear named POODLE)
  • Why TLS combines asymmetric + symmetric encryption — and why neither alone would work
  • The exact TLS 1.2 vs TLS 1.3 handshake, step by step
  • What Perfect Forward Secrecy is and why TLS 1.3 mandates it
  • What a cipher suite actually means (decoded, piece by piece)

Read the full lesson → [link]

#TLS #SSL #Security #Cryptography #Networking #SystemDesign #BackendEngineering #InterviewPrep


TLS/SSL thumbnail


What You'll Learn

  • Why SSL is deprecated and what killed each version — including the POODLE attack on SSL 3.0
  • The difference between symmetric and asymmetric encryption, and why TLS uses both
  • The TLS 1.2 handshake step by step, with a diagram showing 2-RTT flow
  • The TLS 1.3 handshake step by step, with a diagram showing 1-RTT flow and 0-RTT resumption
  • What a cipher suite is and how to read one (TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384)
  • Perfect Forward Secrecy — what it means, why it matters, and why TLS 1.3 mandates it
  • How certificate validation works during the handshake
  • A Node.js TLS configuration example showing how these concepts surface in real code

SSL Is Dead — A Brief History of Protocol Versions

The name "SSL" persists in common speech, but SSL has been dead for years. Understanding what killed each version matters in interviews because it reveals the threat models TLS was designed to address.

PROTOCOL VERSION TIMELINE
─────────────────────────────────────────────────────────────────
SSL 2.0  (1995)  — Netscape's first attempt. Broken by design.
                   Vulnerable to DROWN attack (2016 disclosure).
                   Deprecated: RFC 6176 (2011).

SSL 3.0  (1996)  — Replaced SSL 2.0. Widely deployed.
                   Broken by POODLE (2014): CBC padding oracle
                   allows attacker to decrypt 1 byte per 256
                   requests. Fatal for HTTPS.
                   Deprecated: RFC 7568 (2015).

TLS 1.0  (1999)  — First TLS version. Based on SSL 3.0 internally.
                   Vulnerable to BEAST attack (CBC mode in TLS 1.0).
                   Deprecated: RFC 8996 (2021). Removed by
                   major browsers in 2020.

TLS 1.1  (2006)  — Fixed BEAST. Added explicit IVs.
                   No significant new features.
                   Deprecated: RFC 8996 (2021). Same browser
                   removal timeline as TLS 1.0.

TLS 1.2  (2008)  — Current supported minimum. Introduced:
                   SHA-256, AEAD cipher modes (GCM),
                   extensible cipher suite negotiation.
                   Still safe when configured correctly.
                   Required by PCI DSS, HIPAA.

TLS 1.3  (2018)  — Major redesign. Introduced:
                   1-RTT handshake (vs 2-RTT for TLS 1.2),
                   mandatory Perfect Forward Secrecy,
                   removed weak algorithms entirely,
                   0-RTT session resumption.
                   Mandated by new compliance frameworks.
─────────────────────────────────────────────────────────────────

The key distinction: TLS is not "SSL with a version bump." TLS 1.3 is a fundamentally different protocol — it dropped the compatibility with SSL-era design choices that enabled attack after attack. The name "SSL" in tools like OpenSSL is historical artifact; the actual protocol negotiated on the wire is TLS.


Symmetric vs Asymmetric Encryption

TLS uses two fundamentally different types of encryption. Understanding why both are necessary — and why neither alone works — is the key to understanding TLS.

Symmetric Encryption

One key. The same key encrypts and decrypts.

  SYMMETRIC ENCRYPTION
  ─────────────────────────────────────────────────────────────────
  Plaintext ──[AES-256-GCM key]──► Ciphertext
  Ciphertext ──[AES-256-GCM key]──► Plaintext (same key!)
  ─────────────────────────────────────────────────────────────────

Characteristics:

  • Extremely fast — hardware-accelerated on modern CPUs (AES-NI instruction set)
  • AES-256-GCM can encrypt/decrypt at memory-bus speeds (gigabytes per second per core)
  • The key is typically 128–256 bits
  • Example algorithms: AES-128-GCM, AES-256-GCM, ChaCha20-Poly1305

The fatal problem for the internet: How do you share the key with the server in the first place? If you send the key over the network before encryption is established, an attacker intercepting the connection gets the key and can decrypt everything. This is the key distribution problem.

Asymmetric Encryption

Two keys — mathematically linked. What one key encrypts, only the other can decrypt.

  ASYMMETRIC ENCRYPTION
  ─────────────────────────────────────────────────────────────────
  Public key:  Published openly. Anyone can have it.
  Private key: Known only to the server. Never leaves the server.

  Anyone:    Plaintext ──[server's public key]──► Ciphertext
  Server:  Ciphertext ──[server's private key]──► Plaintext

  The public key CANNOT decrypt what it encrypted.
  ─────────────────────────────────────────────────────────────────

Characteristics:

  • Computationally expensive — 100x to 1000x slower than symmetric encryption
  • RSA-2048: encrypting is fast, decrypting is slow. Not suitable for bulk data.
  • ECDH (Elliptic Curve Diffie-Hellman): key agreement without directly encrypting/decrypting data
  • Example algorithms: RSA-2048, RSA-4096, ECDH with P-256, ECDH with X25519

Solves the key distribution problem: The public key is public. Anyone can use it. An attacker intercepting the public key gains nothing — they cannot decrypt data with it.

Why TLS Combines Both

Neither approach alone works for securing internet traffic:

ApproachProblem
Symmetric onlyHow do you share the key securely before encryption exists?
Asymmetric onlyToo slow for bulk data. Encrypting a 5MB file with RSA takes seconds.
Symmetric + AsymmetricUse asymmetric encryption to securely exchange a symmetric key, then use the symmetric key for all actual data. Fast AND secure.

TLS's solution:

  1. Use asymmetric cryptography (RSA or ECDH) during the handshake to agree on a shared secret — without ever transmitting that secret
  2. Derive a symmetric session key from that shared secret
  3. Use the symmetric session key (AES-256-GCM or ChaCha20-Poly1305) for all actual data transfer

The asymmetric step happens once per connection. The symmetric step happens for every byte of data. This is why TLS is fast in practice — bulk data never touches the slow asymmetric algorithms.


Cipher Suites — Decoded

A cipher suite is a named combination of algorithms that TLS uses for a specific connection. During the handshake, the client offers a list of cipher suites it supports; the server picks the strongest one it also supports.

TLS 1.2 Cipher Suite Format

TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
│    │     │        │       │   │
│    │     │        │       │   └── MAC algorithm
│    │     │        │       │       (HMAC with SHA-384 for integrity)
│    │     │        │       │
│    │     │        │       └── Encryption algorithm + mode
│    │     │        │           (AES with 256-bit key in GCM mode)
│    │     │        │
│    │     │        └── "WITH" separator
│    │     │
│    │     └── Authentication algorithm
│    │          (RSA certificate validates the server's identity)
│    │
│    └── Key exchange algorithm
│         (ECDHE = Elliptic Curve Diffie-Hellman Ephemeral)
│         (Ephemeral = new key pair per connection → PFS)

└── Protocol

Breaking down each component:

Key Exchange (ECDHE): How client and server agree on the symmetric session key without transmitting it. ECDHE means both sides generate a temporary (ephemeral) ECDH key pair for this connection only, then derive a shared secret. Neither side's long-term private key is used in the actual key material.

Authentication (RSA): How the client verifies it is talking to the real server and not an impersonator. The server presents a certificate signed with its RSA private key; the client verifies the signature using the public key in the certificate.

Encryption (AES_256_GCM): The symmetric algorithm used for bulk data. AES-256 in GCM (Galois/Counter Mode) — an AEAD (Authenticated Encryption with Associated Data) mode that provides both confidentiality and integrity in one pass.

MAC (SHA384): Message Authentication Code algorithm for verifying data integrity. In AEAD modes (GCM), this is partly redundant with the built-in authentication tag — GCM already provides integrity. TLS 1.3 removed the separate MAC field entirely for AEAD suites.

TLS 1.3 Cipher Suite Format

TLS 1.3 radically simplified cipher suites by separating key exchange and authentication from the symmetric cipher:

TLS_AES_256_GCM_SHA384
│    │       │   │
│    │       │   └── HKDF hash algorithm
│    │       │       (used for key derivation)
│    │       │
│    │       └── AEAD cipher
│    │           (symmetric encryption + integrity)
│    │
│    └── Cipher name

└── Protocol

TLS 1.3 only has 5 cipher suites (down from hundreds in TLS 1.2). The key exchange method is always ECDHE or DHE — negotiated separately. This means all TLS 1.3 connections automatically have Perfect Forward Secrecy.


The TLS 1.2 Handshake — 2-RTT

Before any encrypted data flows, TLS 1.2 requires two full round trips.

TLS 1.2 HANDSHAKE
─────────────────────────────────────────────────────────────────────────
CLIENT                                                           SERVER
  │                                                                │
  │  ◄────────────────── TCP 3-way handshake ──────────────────►  │
  │                    (SYN, SYN-ACK, ACK)                        │
  │                                                                │
  │                         [RTT 1 begins]                        │
  │                                                                │
  │  ── ClientHello ──────────────────────────────────────────►   │
  │     • TLS version supported (max: 1.2)                        │
  │     • Random nonce (client_random, 32 bytes)                  │
  │     • Supported cipher suites (ordered by preference)         │
  │     • Supported compression methods                           │
  │     • Extensions (SNI, ALPN, session resumption ticket...)    │
  │                                                                │
  │  ◄── ServerHello ─────────────────────────────────────────    │
  │     • Chosen TLS version                                      │
  │     • Random nonce (server_random, 32 bytes)                  │
  │     • Chosen cipher suite                                      │
  │     • Session ID (for resumption)                             │
  │                                                                │
  │  ◄── Certificate ─────────────────────────────────────────    │
  │     • Server's X.509 certificate chain                        │
  │     • Client validates: CA signature, expiry, hostname        │
  │                                                                │
  │  ◄── ServerHelloDone ──────────────────────────────────────   │
  │     • Signals end of server's handshake messages              │
  │                                                                │
  │                        [RTT 1 complete]                       │
  │                                                                │
  │                        [RTT 2 begins]                         │
  │                                                                │
  │  ── ClientKeyExchange ────────────────────────────────────►   │
  │     • Pre-master secret encrypted with server's public key    │
  │       (RSA key exchange) OR                                   │
  │     • Client's ECDH public key (ECDHE key exchange)          │
  │     Both sides now derive the master secret independently     │
  │     using: master_secret = PRF(pre_master, client_random,     │
  │                                server_random)                 │
  │                                                                │
  │  ── ChangeCipherSpec ─────────────────────────────────────►   │
  │     • "I'm switching to encrypted mode now"                   │
  │                                                                │
  │  ── Finished ─────────────────────────────────────────────►   │
  │     • HMAC over all handshake messages (encrypted)            │
  │     • Proves client derived the same keys as server           │
  │                                                                │
  │  ◄── ChangeCipherSpec ─────────────────────────────────────   │
  │  ◄── Finished ─────────────────────────────────────────────   │
  │     • Server's HMAC over all handshake messages (encrypted)   │
  │     • Proves server derived the same keys as client           │
  │                                                                │
  │                        [RTT 2 complete]                       │
  │                                                                │
  │  ◄══════════════ Encrypted Application Data ════════════════► │
  │                  (AES-256-GCM, session key)                   │
  │                                                                │
─────────────────────────────────────────────────────────────────────────
TOTAL: 2 RTTs after TCP handshake (or 3 RTTs including TCP SYN)

Key points about TLS 1.2:

  • The client generates the pre-master secret and encrypts it with the server's public key (RSA) — or uses ECDHE to agree on it without transmitting it directly
  • Both sides independently derive the same session keys using a pseudo-random function (PRF) seeded with the pre-master secret and both random nonces
  • The Finished messages are the first encrypted messages — they verify both sides computed identical keys
  • Total overhead before data: 1 TCP RTT + 2 TLS RTTs = minimum 3 round trips on a new connection

The TLS 1.3 Handshake — 1-RTT

TLS 1.3 collapses the handshake to a single round trip by doing aggressive front-loading.

TLS 1.3 HANDSHAKE
─────────────────────────────────────────────────────────────────────────
CLIENT                                                           SERVER
  │                                                                │
  │  ◄────────────────── TCP 3-way handshake ──────────────────►  │
  │                    (SYN, SYN-ACK, ACK)                        │
  │                                                                │
  │                        [RTT 1 begins]                         │
  │                                                                │
  │  ── ClientHello ──────────────────────────────────────────►   │
  │     • TLS version: 1.3 (via supported_versions extension)     │
  │     • Random nonce (client_random)                            │
  │     • Supported cipher suites                                 │
  │     • key_share extension: client's ECDH public key           │
  │       (client generates ephemeral key pair immediately)       │
  │     • SNI, ALPN, and other extensions                         │
  │                                                                │
  │  ◄── ServerHello ─────────────────────────────────────────    │
  │     • Chosen cipher suite                                     │
  │     • key_share: server's ECDH public key                     │
  │       (server generates its own ephemeral key pair)           │
  │     Both sides NOW compute the shared secret independently:   │
  │     shared_secret = ECDH(client_private, server_public)       │
  │                   = ECDH(server_private, client_public)       │
  │                   (same result, math guarantees it)           │
  │                                                                │
  │  ◄── {Certificate} ────────────────────────────────────────   │
  │     (encrypted with handshake traffic key)                    │
  │                                                                │
  │  ◄── {CertificateVerify} ──────────────────────────────────   │
  │     • Server signs handshake transcript with its private key  │
  │     • Client verifies: confirms server owns the private key   │
  │       matching the certificate's public key                   │
  │                                                                │
  │  ◄── {Finished} ───────────────────────────────────────────   │
  │     • HMAC over handshake (encrypted)                         │
  │                                                                │
  │                        [RTT 1 complete]                       │
  │                                                                │
  │  ── {Finished} ───────────────────────────────────────────►   │
  │     • Client's HMAC over handshake (encrypted)                │
  │                                                                │
  │  ◄══════════════ Encrypted Application Data ════════════════► │
  │            (AES-256-GCM or ChaCha20-Poly1305)                 │
  │                                                                │
  { } = encrypted with derived handshake key                      │
─────────────────────────────────────────────────────────────────────────
TOTAL: 1 RTT after TCP handshake (or 2 RTTs including TCP SYN)

What TLS 1.3 changed to enable 1-RTT:

  1. Key share in ClientHello: The client no longer waits to see the server's cipher suite choice before sending key material. It sends ECDH public keys for the cipher suites it is likely to support. If the server picks a different group, it sends a HelloRetryRequest (a rare extra RTT), but this almost never happens.

  2. Certificate and Finished encrypted immediately: The server encrypts the Certificate and Finished messages with a key derived from the ECDH exchange — before the client has authenticated anything. This means the certificate is private (an attacker watching traffic cannot see the domain being connected to... if SNI is also encrypted, which ESNI/ECH addresses separately).

  3. Removed all weak algorithms: TLS 1.3 dropped RSA key exchange entirely (still used for authentication via certificates), static DH, RC4, DES, 3DES, SHA-1, and MD5. All remaining cipher suites are AEAD-based, all support Perfect Forward Secrecy.

TLS 1.3 — 0-RTT Session Resumption

When a client reconnects to a server it has previously connected to, TLS 1.3 supports 0-RTT data (also called Early Data): the client sends application data in the very first flight, before any server response.

TLS 1.3 — 0-RTT RESUMPTION
─────────────────────────────────────────────────────────────────────────
(Previous connection stored a PSK — Pre-Shared Key / resumption ticket)

CLIENT                                                           SERVER
  │                                                                │
  │  ── ClientHello ──────────────────────────────────────────►   │
  │     • psk_identity: session ticket from last connection       │
  │     • early_data extension: "I'm sending 0-RTT data"         │
  │     • key_share: new ECDH key for this connection             │
  │                                                                │
  │  ── {Early Data (0-RTT)} ─────────────────────────────────►   │
  │     • Encrypted with PSK-derived key                          │
  │     • Arrives at server before server has responded           │
  │                                                                │
  │  ◄── ServerHello + Certificate + Finished ────────────────    │
  │  ◄── {early_data accepted / rejected} ─────────────────────   │
  │                                                                │
  │  ◄══════════════ Normal 1-RTT encrypted data ════════════════►│
─────────────────────────────────────────────────────────────────────────

The 0-RTT caveat — replay attacks: 0-RTT data can be replayed by an attacker. If an attacker intercepts the ClientHello + early data, they can re-send it to the server, causing the server to process the same request twice. For a GET /news/latest this is harmless. For a POST /payment or DELETE /account, this is catastrophic. 0-RTT should only be used for idempotent, read-only requests.


Perfect Forward Secrecy

Perfect Forward Secrecy (PFS) is the property that compromise of a server's long-term private key does not allow an attacker to decrypt past recorded sessions.

Why it matters without PFS:

Imagine an attacker recording all encrypted traffic between you and your bank for five years. They cannot decrypt it now because they do not have the private key. But the moment they obtain the private key — whether by hacking the server, buying it from a rogue employee, or being compelled by a court order — they can go back and decrypt every session from the past five years. This is a realistic threat model: intelligence agencies and sophisticated attackers routinely record encrypted traffic hoping to decrypt it later.

How TLS 1.2 without PFS fails:

WITHOUT PFS (RSA key exchange in TLS 1.2)
──────────────────────────────────────────────────────────────
Session key derivation:
  session_key = derived_from(pre_master_secret)
  pre_master_secret = RSA_decrypt(encrypted_pre_master, server_private_key)

If attacker has server_private_key AND recorded traffic:
  → They can compute pre_master_secret
  → They can derive session_key
  → They can decrypt all recorded sessions
──────────────────────────────────────────────────────────────

How PFS (ECDHE) prevents this:

WITH PFS (ECDHE in TLS 1.2 or TLS 1.3)
──────────────────────────────────────────────────────────────
Per-connection key generation:
  client_ephemeral_keypair = generate_ecdh_keypair()   ← thrown away after handshake
  server_ephemeral_keypair = generate_ecdh_keypair()   ← thrown away after handshake

  shared_secret = ECDH(client_ephemeral_private, server_ephemeral_public)
               = ECDH(server_ephemeral_private, client_ephemeral_public)

  session_key = HKDF_derive(shared_secret, ...)

The ephemeral private keys never leave memory.
They are deleted as soon as the handshake completes.

If attacker has server's long-term private key AND recorded traffic:
  → Long-term private key was not used to derive session_key
  → Ephemeral private keys were deleted
  → Attacker CANNOT reconstruct session_key
  → Past sessions remain encrypted
──────────────────────────────────────────────────────────────

TLS 1.3 mandates PFS. All TLS 1.3 key exchanges use ephemeral ECDH or DHE — static RSA key exchange is not available. If your server runs TLS 1.3 exclusively, every connection has PFS automatically.

For TLS 1.2, PFS requires deliberately choosing cipher suites with ECDHE or DHE — and explicitly disabling TLS_RSA_* cipher suites, which do not provide PFS.


Certificate Validation During the Handshake

When the server sends its certificate, the client performs several checks before trusting the connection:

CERTIFICATE VALIDATION CHAIN
─────────────────────────────────────────────────────────────────
Certificate received from server:
  Subject:   CN=*.bank.com
  Issuer:    DigiCert TLS RSA SHA256 2020 CA1
  Valid:     2024-01-01 to 2025-01-01
  Public key: RSA 2048-bit (used for authentication)
  Signature: signed by issuer's private key

Client checks:
  1. HOSTNAME: Does CN or SAN match "bank.com"?
               *.bank.com → yes, wildcard matches.

  2. EXPIRY:   Is today between Valid From and Valid To?
               If expired: connection fails.

  3. CHAIN:    Is the Issuer cert signed by a trusted CA?
               DigiCert TLS RSA SHA256 2020 CA1
               └── signed by DigiCert Root CA G5
                   └── in OS/browser trusted root store? YES → valid.

  4. REVOCATION: Is the cert revoked? (CRL or OCSP check)
                  OCSP Stapling: server pre-fetches proof from CA.

  5. SIGNATURE: Does the cert's signature verify against the
                issuer's public key? If tampered → fails.

All checks pass → client trusts this server's public key
─────────────────────────────────────────────────────────────────

In TLS 1.3, the server also sends a CertificateVerify message — a signature over the entire handshake transcript using its private key. This proves the server actually possesses the private key corresponding to the certificate's public key (ownership proof), not just that it has a copy of the certificate.


Code Example — TLS Configuration in Node.js

This example shows how the concepts above map to real configuration options.

const https  = require("https");
const tls    = require("tls");
const fs     = require("fs");

// ── Server: configure which TLS versions and cipher suites are allowed ──

const server = https.createServer({
  // Certificate and private key (used for authentication + signature)
  cert: fs.readFileSync("/etc/ssl/certs/server.crt"),
  key:  fs.readFileSync("/etc/ssl/private/server.key"),

  // Optional: require client certificates (mutual TLS / mTLS)
  // requestCert: true,
  // ca: fs.readFileSync("/etc/ssl/certs/client-ca.crt"),

  // Restrict to TLS 1.2+ — never negotiate TLS 1.0, TLS 1.1, or SSL
  minVersion: "TLSv1.2",
  maxVersion: "TLSv1.3",

  // Cipher suites for TLS 1.2 (TLS 1.3 suites are always enabled and non-configurable)
  // Order matters: server prefers first entry in list
  // All of these have ECDHE → Perfect Forward Secrecy guaranteed
  // All use GCM → AEAD (no separate MAC needed)
  // RSA_WITH_* (static RSA key exchange, no PFS) deliberately excluded
  ciphers: [
    "TLS_AES_256_GCM_SHA384",          // TLS 1.3
    "TLS_CHACHA20_POLY1305_SHA256",    // TLS 1.3 (fast on devices without AES-NI)
    "TLS_AES_128_GCM_SHA256",          // TLS 1.3
    "ECDHE-RSA-AES256-GCM-SHA384",     // TLS 1.2 + PFS
    "ECDHE-RSA-AES128-GCM-SHA256",     // TLS 1.2 + PFS
    "ECDHE-RSA-CHACHA20-POLY1305",     // TLS 1.2 + PFS (mobile-friendly)
  ].join(":"),

  // Server-side cipher preference: server's order wins over client's order
  // Ensures strong ciphers are chosen even if a client prefers weaker ones
  honorCipherOrder: true,

  // Disable session tickets for strict PFS enforcement.
  // Session tickets allow 0-RTT resumption in TLS 1.2 but reuse a ticket key.
  // If the ticket key is compromised, past sessions can be decrypted.
  // For TLS 1.3, session tickets use per-connection PSKs — safer.
  // sessionTimeout: 300,  // seconds; 0 disables server-side session cache
}, (req, res) => {
  // Log which TLS version and cipher suite this connection negotiated
  const socket = req.socket;
  console.log("TLS version:", socket.getProtocol());   // e.g. "TLSv1.3"
  console.log("Cipher suite:", socket.getCipher());    // e.g. { name: "TLS_AES_256_GCM_SHA384", ... }
  console.log("PFS active:  ", socket.getEphemeralKeyInfo() !== null);

  res.writeHead(200);
  res.end("Secure response\n");
});

server.listen(443, () => console.log("HTTPS server running on port 443"));


// ── Client: how to configure outgoing TLS connections ──

function makeSecureRequest(hostname, path) {
  return new Promise((resolve, reject) => {
    const req = https.request({
      hostname,
      port: 443,
      path,
      method: "GET",

      // Client-side TLS options
      minVersion: "TLSv1.2",

      // CA bundle: by default Node uses its built-in Mozilla CA bundle
      // For internal services with self-signed certs, provide the CA explicitly:
      // ca: fs.readFileSync("/etc/ssl/certs/internal-ca.crt"),

      // NEVER use this in production — disables all certificate validation
      // rejectUnauthorized: false,  // ← dangerous; fine for local dev only
    }, (res) => {
      const tlsSocket = res.socket;
      console.log(`Connected to ${hostname}`);
      console.log(`  Protocol:   ${tlsSocket.getProtocol()}`);
      console.log(`  Cipher:     ${tlsSocket.getCipher().name}`);
      console.log(`  Authorized: ${tlsSocket.authorized}`);  // false if cert invalid
      if (!tlsSocket.authorized) {
        console.error(`  Auth error: ${tlsSocket.authorizationError}`);
      }

      let body = "";
      res.on("data", chunk => body += chunk);
      res.on("end", () => resolve(body));
    });

    req.on("error", reject);
    req.end();
  });
}

// Usage:
makeSecureRequest("api.example.com", "/data")
  .then(body => console.log("Response:", body))
  .catch(err => console.error("TLS error:", err.message));


// ── OpenSSL CLI — inspect a server's TLS configuration ──
//
// Check which TLS versions and cipher suites a server supports:
//
//   openssl s_client -connect example.com:443 -tls1_2
//   openssl s_client -connect example.com:443 -tls1_3
//
// View certificate details:
//   openssl s_client -connect example.com:443 -showcerts < /dev/null 2>/dev/null \
//     | openssl x509 -text -noout
//
// Test specific cipher suite:
//   openssl s_client -connect example.com:443 -cipher ECDHE-RSA-AES256-GCM-SHA384
//
// Check if server enforces TLS 1.3 (should fail if not supported):
//   openssl s_client -connect example.com:443 -no_tls1_3

Key code observations:

  • minVersion: "TLSv1.2" is the minimum safe setting. Never allow TLS 1.0/1.1 or SSL.
  • The cipher list deliberately excludes TLS_RSA_* patterns — those have no PFS.
  • rejectUnauthorized: false disables all certificate validation. This is only acceptable in isolated local development — in CI, staging, or production it creates a man-in-the-middle vulnerability as severe as having no TLS at all.
  • socket.authorized lets you check at runtime whether the peer's certificate was validated. A value of false with no error thrown means rejectUnauthorized was disabled — a silent security hole.

Common Mistakes

Mistake 1 — Using "SSL" and "TLS" interchangeably in code comments and configuration

In casual conversation, calling HTTPS "SSL" is understandable. In code, configuration files, and engineering discussions, the distinction matters. SSLv2 and SSLv3 are cryptographically broken protocols — if your nginx or Apache config says SSLProtocol all without explicitly disabling SSL versions, you may be offering broken protocols to clients. Always specify ssl_protocols TLSv1.2 TLSv1.3; (nginx) or SSLProtocol TLSv1.2 TLSv1.3 (Apache) explicitly. The name of the module (mod_ssl, openssl) is legacy naming; the protocol you want is TLS.

Mistake 2 — Assuming TLS 1.3's 0-RTT is always safe

0-RTT data is sent before the server's handshake response. It cannot be protected against replay attacks: an attacker who records a ClientHello + early data can replay it to the server at any time. A replayed GET /home is harmless. A replayed POST /transfer?amount=5000&to=attacker is a critical financial vulnerability. Only use 0-RTT for requests that are explicitly idempotent and have no side effects (e.g., loading static content). Treat 0-RTT like HTTP GET semantics — the request must be safe to repeat multiple times with identical results.

Mistake 3 — Disabling certificate validation in development and forgetting to re-enable it

rejectUnauthorized: false (Node.js), InsecureSkipVerify: true (Go), or -k/--insecure (curl) disable all certificate validation. This makes development easier when working with self-signed certificates — but when these flags reach production (in environment variables, Docker configs, CI pipelines copying dev settings), the application is entirely vulnerable to man-in-the-middle attacks. The correct solution for development is to use a local CA (like mkcert) that generates locally-trusted certificates. The application code stays secure; the OS trust store is updated instead.


Interview Questions

Q1: What is the difference between SSL and TLS?

SSL (Secure Sockets Layer) is the predecessor to TLS (Transport Layer Security). SSL 2.0 (1995) and SSL 3.0 (1996) were developed by Netscape. Both are now deprecated and cryptographically broken — SSL 3.0 was killed by the POODLE attack in 2014 and formally deprecated by RFC 7568 in 2015. TLS is the successor protocol, standardized by the IETF. TLS 1.2 (2008) is the current minimum-acceptable version; TLS 1.3 (2018) is the recommended standard. When someone says "SSL" today, they almost always mean TLS. The libraries are still named OpenSSL and the configuration directive is still ssl_, but the wire protocol is TLS.

Q2: Why does TLS use both symmetric and asymmetric encryption?

Neither type alone works for securing internet traffic. Symmetric encryption (e.g., AES-256-GCM) is extremely fast — hardware-accelerated, capable of gigabytes per second — but requires both parties to share a secret key before encryption begins. On the internet, you cannot securely share that key without already having encryption. Asymmetric encryption (e.g., RSA, ECDH) solves the key distribution problem: the public key is shared openly, and anything encrypted with it can only be decrypted by the corresponding private key. But asymmetric operations are 100x–1000x slower than symmetric — unsuitable for encrypting gigabytes of video or file data. TLS combines both: asymmetric cryptography during the handshake to establish a shared secret without transmitting it, then symmetric encryption using a session key derived from that secret for all actual data. You get the security of asymmetric key exchange with the speed of symmetric bulk encryption.

Q3: What is Perfect Forward Secrecy and why does it matter?

Perfect Forward Secrecy (PFS) is the property that compromise of the server's long-term private key does not allow an attacker to decrypt previously recorded sessions. Without PFS (e.g., TLS 1.2 with RSA key exchange), the session key is derived from a pre-master secret encrypted with the server's RSA public key. If an attacker records all traffic today and later obtains the private key — through a breach, legal compulsion, or insider threat — they can decrypt all past sessions. With PFS (ECDHE), both client and server generate fresh ephemeral key pairs for each connection. The session key is derived via ECDH using those ephemeral keys, which are deleted immediately after the handshake. The long-term private key is only used for authentication (signing), not for key derivation. Even with the private key and the recorded traffic, an attacker cannot reconstruct the ephemeral keys and therefore cannot decrypt past sessions. TLS 1.3 mandates PFS by removing all non-ephemeral key exchange algorithms.

Q4: What changed between TLS 1.2 and TLS 1.3?

The major changes: (1) Handshake speed — TLS 1.3 requires 1-RTT (down from 2-RTT in TLS 1.2), reducing connection setup time by one full network round trip. (2) Mandatory PFS — TLS 1.3 removed static RSA key exchange entirely; all connections use ephemeral ECDH, guaranteeing PFS. (3) Removed weak algorithms — RC4, DES, 3DES, SHA-1, MD5, and export-grade cipher suites are gone. The cipher suite list shrank from hundreds of combinations to 5 carefully chosen options. (4) Encrypted handshake — In TLS 1.3, the Certificate and CertificateVerify messages are encrypted with a key derived early in the handshake, hiding the server's identity from passive observers. (5) 0-RTT resumption — TLS 1.3 introduces 0-RTT early data for reconnecting clients, reducing latency to near-zero for repeat connections (with replay attack caveats). (6) Simplified cipher suite format — Key exchange and authentication are separated from the symmetric cipher, eliminating hundreds of poorly-chosen combinations.

Q5: What is a cipher suite?

A cipher suite is a named combination of cryptographic algorithms that TLS will use for a specific connection. It specifies: (1) the key exchange algorithm — how both sides derive a shared secret without transmitting it (e.g., ECDHE, DHE); (2) the authentication algorithm — how the server proves its identity (e.g., RSA, ECDSA); (3) the bulk encryption algorithm — the symmetric cipher used for data (e.g., AES-256-GCM, ChaCha20-Poly1305); and (4) the MAC/hash algorithm — for integrity verification and key derivation (e.g., SHA-384). For example, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 means: use ECDHE for key exchange, RSA for certificate authentication, AES-256-GCM for data encryption, and SHA-384 for the HMAC. During the TLS handshake, the client sends an ordered list of cipher suites it supports, and the server picks the strongest mutually supported option. TLS 1.3 simplified this to 5 options (e.g., TLS_AES_256_GCM_SHA384) with key exchange and authentication negotiated separately.


Quick Reference — Cheat Sheet

TLS Version Comparison

VersionYearStatusRTTNotes
SSL 2.01995Dead (RFC 6176, 2011)Broken by design
SSL 3.01996Dead (RFC 7568, 2015)POODLE attack
TLS 1.01999Deprecated (RFC 8996, 2021)2BEAST attack
TLS 1.12006Deprecated (RFC 8996, 2021)2No major improvements
TLS 1.22008Supported minimum2Safe with ECDHE ciphers
TLS 1.32018Recommended1 (0-RTT resume)Mandatory PFS, fewer attack surfaces

Symmetric vs Asymmetric Encryption

PropertySymmetric (AES-256-GCM)Asymmetric (RSA, ECDH)
Keys1 shared key2 keys: public + private
SpeedVery fast (GB/s with AES-NI)Slow (100x–1000x slower)
Use in TLSBulk data encryptionHandshake key exchange + authentication
Key distributionProblem — must share key firstSolved — public key is public
PFS possibleN/AYes, with ephemeral keys (ECDHE)

TLS 1.2 vs TLS 1.3 Handshake

StepTLS 1.2TLS 1.3
Round trips2-RTT1-RTT
Key material sent inRTT 2 (ClientKeyExchange)RTT 1 (ClientHello key_share)
Certificate encryptedNoYes
Session resumptionSession ID / tickets (1-RTT)0-RTT (with PSK)
PFSOptional (need ECDHE cipher)Mandatory
Weak algo removalNoYes (RC4, DES, SHA-1 gone)

Cipher Suite Breakdown

TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
└─── └──── └── └──────────── └─────
 │    │     │   │             └── HMAC hash (integrity + key derivation)
 │    │     │   └── Encryption: AES 256-bit in GCM mode (AEAD)
 │    │     └── Authentication: RSA certificate
 │    └── Key exchange: Elliptic Curve Diffie-Hellman Ephemeral (PFS!)
 └── Protocol: TLS

TLS 1.3 format (key exchange is separate):

TLS_AES_256_GCM_SHA384
     └──────────── └─────
     │              └── HKDF hash (key derivation)
     └── AEAD cipher (encryption + integrity in one)
Key exchange: always ECDHE or DHE (negotiated via key_share)

Perfect Forward Secrecy — Quick Logic

Without PFS:  session_key derived from long-term private key
              → steal private key → decrypt all past sessions

With PFS:     session_key derived from ephemeral ECDH keys
              (deleted immediately after handshake)
              → steal private key → cannot reconstruct ephemeral keys
              → past sessions remain secure

Previous: Lesson 5.2 — HTTPS → Next: Lesson 5.4 — FTP & SFTP →


This is Lesson 5.3 of the Networking Interview Prep Course — 8 chapters, 32 lessons.

On this page