All topics
Learning hub

SSL/TLS notes for developers

Master SSL/TLS with a curated set of 3 developer notes — core concepts, patterns, and interview prep. Maintained by the DevRecall team.

Save this stack to your DevRecallMore notes
SSL/TLS

How TLS Works: Handshake & Certificates

SSL/TLS: How TLS Works TLS (Transport Layer Security) encrypts communication between client and server. SSL is the predecessor — it's deprecated. When people sa

SSL/TLS: How TLS Works

TLS (Transport Layer Security) encrypts communication between client and server. SSL is the predecessor — it's deprecated. When people say "SSL" today they mean TLS. Current versions: TLS 1.2 (still common) and TLS 1.3 (recommended, faster).

TLS Handshake (TLS 1.3)

TLS 1.3 — 1-RTT handshake (1.2 needed 2 RTTs):

Client → Server:  ClientHello
  - TLS version supported
  - Cipher suites supported (e.g. TLS_AES_256_GCM_SHA384)
  - Client random (nonce)
  - Key share (public key for key exchange, e.g. X25519)

Server → Client:  ServerHello + Certificate + CertificateVerify + Finished
  - Chosen cipher suite
  - Server random
  - Server's key share
  - Certificate chain (server identity proof)
  - Signature over handshake (proves server owns private key)
  - Finished (MAC over entire handshake — detects tampering)

Client → Server:  Finished
  - Client verifies certificate chain
  - Client verifies CertificateVerify signature
  - Derives same session keys using key shares + randoms
  - Sends Finished

→ Encrypted application data flows both ways

Key exchange: Diffie-Hellman (ECDHE) — client and server each contribute
  a key share; session key derived from both. Neither side sends the key.
  Forward secrecy: compromise of server private key doesn't expose past sessions.

Cipher Suites

TLS_AES_256_GCM_SHA384 (TLS 1.3)
  ^^^                                    — key exchange always ECDHE in TLS 1.3
      ^^^^^^^^^^^                        — AES-256-GCM: bulk encryption (AEAD)
                   ^^^^^^                — SHA-384: HKDF hash for key derivation

TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (TLS 1.2)
  ^^^  ^^^^^  ^^^  ^^^^^^^^^^^^^^^  ^^^^^^
  |    |      |    |                Hash for PRF
  |    |      |    Bulk cipher (AEAD)
  |    |      Auth algorithm (server cert type)
  |    Key exchange
  Protocol

Good suites (TLS 1.2):
  ECDHE-RSA-AES256-GCM-SHA384
  ECDHE-ECDSA-AES256-GCM-SHA384

Avoid:
  RC4 (broken)
  3DES (SWEET32 vulnerability)
  CBC mode without Encrypt-then-MAC (POODLE, BEAST)
  NULL cipher suites (no encryption)
  EXPORT cipher suites (deliberately weakened — FREAK, LOGJAM)

TLS 1.3 vs TLS 1.2

  • TLS 1.3: 1-RTT handshake (vs 2-RTT in 1.2) — measurably faster, especially on mobile

  • TLS 1.3: 0-RTT resumption — send data with first packet (replay attack risk; use only for safe GETs)

  • TLS 1.3: forward secrecy mandatory — ECDHE always used, static RSA key exchange removed

  • TLS 1.3: removed weak algorithms — no RSA key exchange, no CBC, no RC4, no SHA-1

  • TLS 1.2: still widely needed for compatibility with older clients/servers

  • Minimum today: TLS 1.2. TLS 1.0 and 1.1 are deprecated (RFC 8996, 2021). Browsers show warnings.

SSL/TLS

Certificate Authorities, PKI & Trust Chains

SSL/TLS: Certificates, CA & PKI X.509 Certificate Structure A certificate contains: Subject: CN=example.com, O=Example Inc, C=US Subject Alt Names (SAN): DNS:ex

SSL/TLS: Certificates, CA & PKI

X.509 Certificate Structure

A certificate contains:
  Subject:          CN=example.com, O=Example Inc, C=US
  Subject Alt Names (SAN): DNS:example.com, DNS:www.example.com, IP:93.184.216.34
  Issuer:           CN=Let's Encrypt R3, O=Let's Encrypt, C=US
  Serial Number:    unique per CA
  Valid From/To:    2024-01-01 to 2024-04-01 (Let's Encrypt: 90 days)
  Public Key:       RSA 2048 or ECDSA P-256
  Signature:        CA signs hash of certificate with its private key
  Key Usage:        Digital Signature, Key Encipherment
  Extended Key Usage: TLS Web Server Authentication

Common Name (CN) is legacy — modern validation uses SAN.
Wildcard: *.example.com — covers one subdomain level (not sub.sub.example.com).

Certificate Types

  • DV (Domain Validation): CA verifies you control the domain (DNS or HTTP challenge). Automated, free (Let's Encrypt). Shows padlock in browser.

  • OV (Organization Validation): CA verifies organization identity via document check. Company name in cert details.

  • EV (Extended Validation): rigorous legal verification. Green bar in old browsers (modern browsers removed the distinction).

  • Wildcard: *.example.com — covers all subdomains at one level. Useful but limits automation.

  • Multi-SAN: one cert covers multiple domains (example.com, api.example.com, shop.example.com). Let's Encrypt supports up to 100 SANs.

  • Self-signed: signed by own private key (no CA). Fine for internal/dev; browsers show security warning.

Trust Chain

Browser validates certificate chain:

  Root CA certificate     ← pre-installed in OS/browser trust store
      ↓ signs
  Intermediate CA cert    ← server must serve this in the chain
      ↓ signs
  Leaf (server) cert      ← presented for your domain

Why intermediates?
  Root CA private keys are kept offline in HSMs (air-gapped vaults).
  Intermediates do day-to-day signing. If an intermediate is compromised,
  root CA revokes it without replacing every root trust store.

Certificate chain validation:
  1. Browser downloads leaf cert from server
  2. Checks signature using issuer's public key
  3. Walks up to root — each cert signed by parent
  4. Finds root in its trust store
  5. Checks: not expired, not revoked (OCSP/CRL), SAN matches hostname

Missing intermediate = handshake failure on some clients (mobile Safari notorious for this).
Use: https://whatsmychaincert.com to check your chain.

Certificate Revocation

  • CRL (Certificate Revocation List): CA publishes list of revoked serial numbers. Clients download periodically. Stale and large.

  • OCSP (Online Certificate Status Protocol): real-time query to CA for revocation status. Adds latency; CA sees every connection.

  • OCSP Stapling: server fetches OCSP response and includes it in TLS handshake. Client doesn't need to contact CA. Better privacy and performance.

  • OCSP Must-Staple: cert extension requiring stapled OCSP. Browser rejects cert if staple missing. Strongest revocation.

  • Soft fail: browsers ignore revocation check failures silently (performance/privacy tradeoff). Hard fail is impractical at scale.

  • Certificate Transparency (CT): all certificates logged to public append-only logs. Browsers require SCT (Signed Certificate Timestamp) proof. Catches mis-issuance.

SSL/TLS

Configuration: Nginx, HSTS, mTLS & Pinning

SSL/TLS: Configuration & Hardening Nginx TLS Configuration server { listen 443 ssl; listen [::]:443 ssl; http2 on; # enable HTTP/2 server_name example.com www.e

SSL/TLS: Configuration & Hardening

Nginx TLS Configuration

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;                          # enable HTTP/2

    server_name example.com www.example.com;

    # Certificate chain (leaf + intermediates)
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Modern TLS configuration (Mozilla Intermediate)
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;     # TLS 1.3 ignores this; off = client chooses

    # Session resumption
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;           # disable for forward secrecy

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
    resolver 1.1.1.1 8.8.8.8 valid=300s;

    # DH params (for DHE cipher suites)
    ssl_dhparam /etc/ssl/dhparam.pem;  # openssl dhparam -out dhparam.pem 2048

    # Security headers
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Frame-Options DENY always;
    add_header X-Content-Type-Options nosniff always;
    add_header Referrer-Policy strict-origin-when-cross-origin always;
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
}

# HTTP → HTTPS redirect
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

HSTS & Preloading

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

max-age=63072000       — browser remembers HTTPS-only for 2 years
includeSubDomains      — applies to all subdomains
preload                — eligible for browser preload list (hardcoded HTTPS, no HTTP ever)

Preload list:
  - Submit at https://hstspreload.org
  - Removes even the first-visit HTTP downgrade risk
  - Irreversible in the short term — ensure ALL subdomains support HTTPS first
  - Currently in Chrome, Firefox, Safari, Edge preload lists

Rollout strategy:
  1. Start: max-age=300 (5 min) — test, ensure no HTTP subdomains
  2. Increase: max-age=86400 (1 day)
  3. Add includeSubDomains: verify all subs have valid certs
  4. Production: max-age=63072000; includeSubDomains
  5. Submit for preload: add preload directive

Mutual TLS (mTLS)

mTLS requires both server and client to present certificates — not just the server. Used for service-to-service authentication in microservices (zero-trust networks), API authentication for partners.

# Server requires client certificate
server {
    ssl_client_certificate /etc/ssl/ca-chain.pem;  # CA that signed client certs
    ssl_verify_client on;
    ssl_verify_depth 2;

    location /api/ {
        # Client cert details in headers for upstream
        proxy_set_header X-Client-CN $ssl_client_s_dn_cn;
        proxy_set_header X-Client-Verify $ssl_client_verify;
        proxy_pass http://backend;
    }
}
# Generate client certificate (dev/testing)
# 1. Create CA
openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt -subj "/CN=My CA"

# 2. Create client key + CSR
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr -subj "/CN=service-a"

# 3. Sign with CA
openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key   -CAcreateserial -out client.crt

# 4. Test
curl --cert client.crt --key client.key https://api.example.com/

Testing & Debugging

# Check certificate details
openssl s_client -connect example.com:443 -servername example.com </dev/null
openssl s_client -connect example.com:443 </dev/null 2>/dev/null | openssl x509 -noout -text

# Check expiry
openssl s_client -connect example.com:443 </dev/null 2>/dev/null   | openssl x509 -noout -dates

# Test specific TLS version
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3

# Online tools
# https://www.ssllabs.com/ssltest/         — full TLS config audit (A+ rating guide)
# https://securityheaders.com              — security headers check
# https://whatsmychaincert.com             — certificate chain validator

# Decode a certificate
openssl x509 -in cert.pem -noout -text
openssl x509 -in cert.pem -noout -subject -issuer -dates -fingerprint

Keep your SSL/TLS knowledge sharp.

Save this stack to your personal DevRecall — add your own notes, track what you're learning, and share what you know with the community.

Get started — free forever