Frontend Labs
⋯

Chapter 32 — Hands-on: inspecting and testing TLS

Why this chapter exists

A security engineer inspects TLS constantly — debugging a handshake failure, auditing a vendor, verifying a fix. This is the toolbox, consolidated.

openssl s_client — the swiss army knife

# Basic connect, show the negotiated params and chain
openssl s_client -connect example.com:443 -servername example.com -brief </dev/null

# Full detail: every handshake message and state transition
openssl s_client -connect example.com:443 -servername example.com \
        -msg -state -debug </dev/null 2>&1 | less

# Just the leaf certificate, decoded
openssl s_client -connect example.com:443 -servername example.com </dev/null \
        2>/dev/null | openssl x509 -noout -text

# The whole chain as presented
openssl s_client -connect example.com:443 -servername example.com \
        -showcerts </dev/null 2>/dev/null

# Force a protocol version (probe support)
openssl s_client -connect example.com:443 -tls1_2 </dev/null
openssl s_client -connect example.com:443 -tls1_1 </dev/null   # should fail now

# Force / probe a key-exchange group (incl. post-quantum, needs OpenSSL 3.5+)
openssl s_client -connect example.com:443 -groups X25519 </dev/null
openssl s_client -connect example.com:443 -groups X25519MLKEM768 </dev/null

# Check OCSP stapling
openssl s_client -connect example.com:443 -status </dev/null 2>/dev/null \
        | grep -A5 "OCSP Response Status"

# SNI matters: without -servername you may get the wrong/default cert
openssl s_client -connect example.com:443 </dev/null 2>/dev/null \
        | openssl x509 -noout -subject

# Test a specific cipher (TLS 1.2)
openssl s_client -connect example.com:443 -cipher 'ECDHE-ECDSA-AES256-GCM-SHA384' </dev/null

# STARTTLS for mail/other protocols
openssl s_client -connect smtp.example.com:587 -starttls smtp </dev/null

# Client certificate (mTLS)
openssl s_client -connect api.example.com:443 -cert client.crt -key client.key </dev/null

# Measure handshake time
openssl s_time -connect example.com:443 -new -time 5

openssl x509 / req / verify — certificate work

# Decode a cert file
openssl x509 -in cert.pem -noout -text
openssl x509 -in cert.pem -noout -subject -issuer -dates -serial -fingerprint -sha256
openssl x509 -in cert.pem -noout -ext subjectAltName

# Decode a CSR
openssl req -in request.csr -noout -text -verify

# Verify a chain
openssl verify -CAfile root.pem -untrusted intermediates.pem leaf.pem
openssl verify -show_chain -CAfile /etc/ssl/cert.pem leaf.pem

# Does this key match this cert? (the moduli / pubkeys must match)
openssl x509 -in cert.pem -noout -pubkey | openssl md5
openssl pkey -in key.pem -pubout       | openssl md5      # same -> they match

# Convert formats
openssl x509 -in cert.pem -outform der -out cert.der
openssl pkcs12 -export -in cert.pem -inkey key.pem -out bundle.p12    # PEM -> PKCS12
openssl pkcs12 -in bundle.p12 -nodes -out all.pem                     # PKCS12 -> PEM

# CRL / OCSP by hand
openssl crl -in list.crl -noout -text
openssl ocsp -issuer intermediate.pem -cert leaf.pem -text \
        -url http://ocsp.the-ca.example

Purpose-built tools

# testssl.sh -- comprehensive, offline-capable, scriptable
docker run --rm -ti drwetter/testssl.sh https://example.com
testssl.sh --severity HIGH --jsonfile out.json https://example.com

# sslyze -- fast, Python, great for CI gates
pipx run sslyze example.com:443
pipx run sslyze --json_out r.json --mozilla_config=intermediate example.com

# nmap TLS scripts
nmap --script ssl-enum-ciphers,ssl-cert -p 443 example.com

# hey / curl for behaviour
curl -v --tlsv1.3 --tls-max 1.3 https://example.com
curl --cacert myca.pem --cert client.pem --key client.key https://api.example.com

# Wireshark: filter tls.handshake ; with SSLKEYLOGFILE set, decrypt your own
SSLKEYLOGFILE=/tmp/k.log curl -s https://example.com -o /dev/null

Reading a testssl.sh / SSL Labs report

   Look for, in rough priority:
     * Protocols: TLS 1.3 yes, 1.2 yes, everything below NO
     * Cipher categories: only AEAD; no CBC/RC4/3DES/EXPORT/NULL/anon
     * Forward secrecy: on all suites
     * Cert: not expiring soon, SANs correct, chain complete, sane key size,
       SHA-256 signature, CT SCTs present
     * Vulnerabilities section: all "not vulnerable" (Heartbleed, ROBOT,
       CCS, Ticketbleed, BEAST, POODLE, Lucky13, Sweet32, DROWN, Logjam,
       FREAK, CRIME, BREACH*)  (*BREACH is app-level -- separate fix)
     * HSTS present, long max-age
     * OCSP stapling present
   Grade target: A or A+. Anything less, read WHY and fix it.

Practice / capstone lab for Part 7 (90 min)

   Build "SecureShop's front door" locally and prove it's sound:

   1. Using your Ch 29 mini-CA, issue an ECDSA P-256 leaf for
      shop.securesh.op (SAN: shop.securesh.op, www.securesh.op).
   2. Add shop.securesh.op -> 127.0.0.1 to /etc/hosts. Add ca.crt to a
      trust store (or use --cacert everywhere).
   3. Run a TLS 1.3-only server (nginx, Caddy, or `openssl s_server`)
      serving a "hello" page, with:
        - TLS 1.3 + 1.2 (ECDHE + AEAD only), 1.0/1.1 disabled
        - HSTS header (max-age 1 year, includeSubDomains)
        - OCSP stapling if your setup supports it
        - HTTP :80 -> 301 redirect to HTTPS
   4. Add an /admin location that requires mTLS (client cert from your CA).
   5. Verify with: curl (trusted + -k), openssl s_client -msg,
      testssl.sh, sslyze. Screenshot an A-grade-equivalent result.
   6. Now BREAK each control and confirm the tools catch it:
        - enable TLS 1.0        -> testssl flags it
        - serve without the intermediate -> curl fails, browsers may not
        - use an expired leaf   -> verify fails
        - wrong SAN             -> hostname mismatch
        - remove HSTS           -> sslstrip becomes possible (explain)
        - drop -Verify on /admin-> anyone reaches admin
   7. Write it up in ~/sec-lab/reports/part7-tls.md: what you built, what
      each tool showed, what each break looked like. This is your first
      real security report -- Part 12 builds on it.

Common confusions

  • "s_client hangs." It's waiting for stdin. Add </dev/null (or type Q + Enter).
  • "Wrong cert returned." You forgot -servername (SNI). Virtual hosts need it.
  • "testssl says vulnerable to BREACH — patch TLS." BREACH is HTTP-response compression + secrets in the body; it's an app fix (disable compression on sensitive responses, add CSRF-token masking), not a TLS setting.
  • "SSL Labs won't scan my internal host." Use testssl.sh or sslyze — they run locally and offline.
  • "My key doesn't match my cert." Compare openssl x509 -pubkey and openssl pkey -pubout; mismatched key/cert is a common deploy error.

Check yourself

  1. What flag makes openssl s_client return immediately instead of hanging, and what flag sets SNI?
  2. How do you check whether a private key matches a certificate?
  3. Which command probes whether a server still supports TLS 1.1?
  4. On a testssl.sh report, name five things you check before calling a config sound.
  5. testssl reports "vulnerable to BREACH" — is that a TLS fix? Why/why not?

Further reading

  • Book (free): OpenSSL Cookbook, Ristić — the s_client / x509 / verify reference. Pair with Appendix B here.
  • Tool docs: testssl.sh wiki, sslyze README, nmap ssl-enum-ciphers.
  • Site: badssl.com (break-test your clients), ssllabs.com/ssltest, hardenize.com.
  • Cheat sheet: "OpenSSL quick reference" posts abound; verify against the 3.x man pages since flags changed from 1.1.
  • Man pages: openssl-s_client(1), openssl-x509(1), openssl-verify(1), openssl-ocsp(1), openssl-s_server(1).

End of Part 7 — Milestone check

  • I can state exactly what HTTPS guarantees and list what it does not
  • I can explain why "the padlock" does not mean "safe"
  • I can draw the TLS 1.3 handshake from memory and say what each message proves
  • I know why TLS 1.3 always has forward secrecy and TLS 1.2 might not
  • I can read a certificate and know which field does hostname matching
  • I built a mini-CA and issued a working leaf certificate
  • I can explain Certificate Transparency, and why revocation mostly doesn't work
  • I can walk through an ACME issuance and I know what a CAA record does
  • I stood up a hardened local HTTPS + mTLS server and graded it
  • I broke each TLS control and watched the tools catch it
  • I wrote the Part 7 TLS report

Generated from real-life-guide.md · 8 Oct 2026 16:36