backend / networking / 12_tls_https_certificates.md

TLS, HTTPS, Certificates

6 interview angles 6 min read source

TLS, HTTPS, Certificates

TLS (Transport Layer Security) encrypts a TCP connection. HTTPS = HTTP over TLS. Modern web is TLS 1.2 and 1.3; everything older (SSL 2/3, TLS 1.0/1.1) is deprecated.

What TLS gives you

  1. Confidentiality — observers see ciphertext.
  2. Integrity — tampering is detected.
  3. Authentication — the server proves it’s who the cert says it is. (Mutual TLS authenticates the client too.)

It does not give you:

  • Authorization. TLS proves identity; your app decides what they can do.
  • Hiding metadata fully. The destination IP, port, packet sizes, SNI hostname (in TLS <1.3 without ECH) are visible.

TLS 1.3 handshake (1-RTT)

Client  ──ClientHello (key share, cipher suites, SNI)──▶  Server
Client  ◀── ServerHello + cert + EncryptedExtensions + Finished ── Server
Client  ──ChangeCipherSpec + Finished──▶  Server
( application data starts; total 1 RTT, 0 RTT for resumption )

TLS 1.2 took 2 RTTs. TLS 1.3 cut it to 1 (and 0 for session resumption). On a 100ms RTT link, that’s saving 100–200ms before HTTP can even start.

Certificate basics

A certificate is a signed claim: “this public key belongs to api.example.com, signed by [CA], valid from X to Y.” Browsers/OSes ship a list of trusted root CAs (~150). A cert is valid if there’s a chain from it back to one of those roots.

[ Root CA cert ]              ← trusted by OS/browser, self-signed
       │ signs
[ Intermediate CA cert ]
       │ signs
[ api.example.com cert ]      ← what your server presents

The server presents the leaf + intermediate(s); the client already trusts the root. Forgetting to include the intermediate is the #1 cert misconfiguration — Chrome may still work (it fetches missing intermediates) but curl and Python’s requests fail.

Certificate types

Type Validates
DV (Domain Validated) you control the domain (HTTP/DNS challenge). Let’s Encrypt is DV.
OV (Organization Validated) DV + manual check that org exists
EV (Extended Validation) OV + extra paperwork. Used to show green-bar in browsers; browsers stopped highlighting it.
Wildcard *.example.com — covers any single-level subdomain
SAN (Subject Alternative Name) one cert covers multiple specific names

For 99% of services, a free DV cert from Let’s Encrypt is fine. EV is largely vestigial.

Let’s Encrypt + ACME

Let’s Encrypt issues free DV certs valid for 90 days. The ACME protocol automates issuance and renewal:

  1. Client (Certbot, acme.sh, Caddy built-in) generates a key and CSR.
  2. Tells Let’s Encrypt “I want a cert for example.com.”
  3. Let’s Encrypt challenges: prove you own it.
    • HTTP-01: serve a token at http://example.com/.well-known/acme-challenge/<token>.
    • DNS-01: create a TXT record _acme-challenge.example.com.
    • TLS-ALPN-01: rare, used when you can’t open port 80.
  4. Let’s Encrypt fetches/queries the proof.
  5. Cert issued. Renewal runs automatically every 60 days.

Caddy and Traefik do this transparently. nginx + certbot is the manual standard. AWS ACM does it for AWS LBs (free, auto-renewed, but only usable on AWS resources).

SNI — multiple sites, one IP

Server Name Indication is a TLS extension where the client sends the hostname during the handshake so the server can pick the right cert. Without SNI, one IP could only host one HTTPS cert.

In TLS 1.2, SNI is plaintext (observers see which site you’re visiting). TLS 1.3 + ECH (Encrypted Client Hello) hides it. ECH is rolling out; Cloudflare supports it server-side, browsers gradually.

mTLS (Mutual TLS)

Server presents a cert; client also presents a cert. Both validate each other.

Use cases:

  • Service-to-service inside a mesh (Istio/Linkerd auto-issue and rotate).
  • High-security APIs (banking, government).
  • Replacing API keys with cryptographic identity.

In Python:

import httpx
client = httpx.Client(cert=("client.crt", "client.key"), verify="ca.crt")
client.get("https://api.example.com/")

Cost: cert distribution and rotation is operationally heavy — service meshes exist mostly to make this manageable.

Cipher suites and PFS

A cipher suite specifies key exchange + bulk encryption + MAC. TLS 1.3 stripped this down — only AEAD ciphers remain (AES-GCM, ChaCha20-Poly1305) and key exchange is always (EC)DHE for forward secrecy.

Forward secrecy means a future leak of the server’s long-term key doesn’t decrypt past traffic — each session uses an ephemeral DH key that’s discarded. Important for compliance and for resisting later attacks.

Common cert errors and what they mean

Error Meaning
CERT_HAS_EXPIRED cert past notAfter. Renew.
UNABLE_TO_VERIFY_LEAF_SIGNATURE missing intermediate cert in the chain
HOSTNAME_MISMATCH cert is for foo.com, you connected to bar.com
CERT_AUTHORITY_INVALID root CA not in client’s trust store (self-signed; or CA you didn’t add)
SSL_ERROR_NO_CYPHER_OVERLAP client and server agreed on no cipher; usually too old TLS version on one side

Python TLS gotchas

import requests
requests.get("https://self-signed.example/", verify=False)   # disables validation — DON'T in prod
requests.get("https://api.example/", verify="/path/to/ca.crt")  # use a private CA bundle
  • requests ships with certifi (Mozilla CA bundle). On corp networks with MITM proxies, point it at the corporate CA bundle via REQUESTS_CA_BUNDLE env var.
  • urllib3.disable_warnings(InsecureRequestWarning) silences the noise but leaves the security hole. Don’t.
  • ssl.create_default_context() is the right starting point for raw socket TLS.
  • Old Python on old systems may default to TLS 1.0 — explicitly set ssl.TLSVersion.TLSv1_2 minimum.

HSTS — force HTTPS

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

Tells the browser “always use HTTPS for this domain for the next year.” Prevents downgrade attacks. preload opts into the browser’s built-in list (irreversible — think before adding).

What gets encrypted, what doesn’t

Encrypted: HTTP request line, headers, body. Cookies, auth tokens. Whatever’s in the TLS payload.

Visible to observers: source/destination IPs and ports, packet sizes and timing, SNI hostname (without ECH), the fact that you connected.

So TLS hides what you’re saying, but a network observer still knows you talked to api.example.com (and roughly how much). For metadata privacy you need Tor or similar.

Common interview confusions

  • “SSL is the same as TLS.” — SSL 1/2/3 are the historical predecessors; TLS 1.0+ is the modern protocol. People still say “SSL” loosely. Correct vocabulary: TLS.
  • “HTTPS encrypts the URL.” — encrypts the path and query (those are inside the HTTP request). Doesn’t encrypt the destination IP/port or the SNI hostname.
  • “A wildcard cert *.example.com covers example.com.” — no. Wildcards cover one subdomain level. *.example.com doesn’t match example.com itself or a.b.example.com. Use a SAN cert with both names.
  • “TLS proves the server is the right company.” — DV proves they control the domain, nothing more. EV/OV add organization checks. Most attacks don’t need to forge a cert; they redirect DNS or phish.

Interview angle

  • “What does TLS provide?” — confidentiality, integrity, server authentication (and client auth in mTLS). Not authorization, not metadata privacy.
  • “Walk through the TLS 1.3 handshake.” — ClientHello (with key share, SNI, cipher list) → ServerHello + cert + Finished → Client Finished + app data. 1 RTT, 0 RTT for resumption.
  • “What’s the cert chain and what’s a common misconfiguration?” — leaf → intermediate → root; root pre-trusted by OS/browser. Common miss: server doesn’t include intermediates, browsers fetch them but curl / requests fail with unable to verify leaf signature.
  • “What’s mTLS and when do you use it?” — both sides present certs. Service-to-service inside a mesh, high-security APIs, replacing static API keys with crypto identity. Cost is cert distribution / rotation — service meshes solve that.
  • “What’s HSTS and what does preload mean?” — header telling browsers to always use HTTPS for this domain. preload adds you to the browser’s hardcoded list — irreversible-ish; opt in with care.
  • “Let’s Encrypt and ACME — how does it work?” — automated DV cert issuance via HTTP-01 or DNS-01 challenge. 90-day certs auto-renewed. Caddy/Traefik do it built-in; nginx pairs with certbot.