What would you like to learn?

Try PowerShell, networks, XSS, or Step 138

Browse the full curriculum →

Networks

Step 160. SSL/TLS and HSTS — The ID-Card System That Stops the Man in the Middle

Step 160Estimated practice · 3 hours

Level 2 — Network Attacks and MITM | Difficulty ★★★☆☆ | Estimated time: 3 hours

Prerequisites: Step 85 (HTTP vs. HTTPS comparison) and Steps 156~159 (ARP/DNS spoofing) complete.

  • What you need: a Linux terminal (WSL is enough), openssl. Everything runs locally with no external connections.
  • Caution: ⚠️ All exercises in this chapter are for your own lab and legal platforms only. Applying them to unauthorized systems is a crime. The self-signed certificate you make today is for learning; used on a real service, browsers will raise warnings. This chapter’s certificate generation and handshake outputs were measured on 2026-09-09 on WSL Linux (OpenSSL 3.x).

In the last chapters, you learned how to become the man in the middle — fooling ARP, intercepting traffic, even swapping out DNS. Yet one wall stands in front of all those attacks: TLS. Today we look not from MITM’s perspective but from that wall’s perspective. Why a certificate is "an ID card that can’t be impersonated," what the handshake actually looks like, and how HSTS rendered sslstrip — an attempt to bypass even that wall — useless. You’ll make a fake certificate yourself and feel in your body why the browser warns.


1. Learning Objectives

By the end of this chapter, you will be able to:

  • Explain the TLS handshake’s stages (Client Hello → certificate → key agreement → encrypted communication)
  • Explain the certificate chain (root CA → intermediate CA → site) and how a self-signed certificate differs
  • Run a local TLS server with openssl and interpret s_client output
  • Explain from the attacker’s perspective why MITM attackers are obsessed with impersonating or stealing certificates
  • Explain the principle by which HSTS blocks sslstrip-style downgrade attacks

2. Background Knowledge — Today’s Tools and Concepts

Today’s Tools at a Glance

Category Details
Language/environment Linux terminal + openssl (no external network needed)
Today’s commands openssl req -x509 (self-signed certificate), openssl x509 -noout -subject -issuer (read a certificate), openssl s_server (practice TLS server), openssl s_client -connect (observe the handshake)
Concepts needed Symmetric/public keys, certificates and CAs, the handshake, cipher suites, HSTS, Step 85’s plaintext/ciphertext contrast
Today’s artifact 1 self-signed certificate + a handshake-interpretation memo + a TLS/HSTS defense-structure diagram

2-1. What TLS Blocks — Contents and Impersonation

As confirmed in Step 85, TLS turns contents into ciphertext. But encryption alone leaves one hole. Is the party I’m speaking encrypted words to really that server? If a MITM attacker cuts in saying "I’m example.com, let’s talk encrypted with me," the victim ends up talking to the attacker, encrypted all the while.

What fills this hole is the certificate. A certificate is a document in which a CA (Certificate Authority) signs a guarantee that "the owner of this domain is the owner of this public key." The browser verifies the signature against a pre-installed CA list (the root store), and only on success does it show the padlock. A certificate the attacker made themselves has no CA signature, so a warning appears — today you’ll create that scene yourself.

2-2. The Certificate Chain — A Relay of Guarantees

The root CAs browsers trust number only a few dozen worldwide, and a root doesn’t sign site certificates directly — it delegates authority to intermediate CAs. So a server usually sends "site certificate + intermediate certificate" together, and the browser verifies by following the chain — site → intermediate → root. If any link is broken or a signature doesn’t match: warning.

The self-signed certificate you make today is one whose issuer and subject are the same — a declaration of "I vouch for myself." Since there’s no trustworthy CA at the end of its chain, browsers don’t trust it.

2-3. The Handshake — The Ceremony That Opens the Encrypted Channel

The TLS handshake is the negotiation that happens before communication.

  1. Client Hello: the client announces "I support these TLS versions and these cipher suites." At this point the SNI (the server name you’re connecting to) rides in plaintext.
  2. Server Hello + certificate: the server picks "then let’s use this cipher suite" and presents its certificate.
  3. Key agreement: the two safely agree on a one-time symmetric key used only for this session.
  4. From then on, all contents become Application Data encrypted with that key.

2-4. sslstrip and HSTS — The Downgrade Attack and Its End

A famous bypass of the past is sslstrip. Users usually don’t type https:// into the address bar, so the first connection is HTTP. MITM swapped every HTTPS link and redirect in that first HTTP response to HTTP, keeping the user communicating in plaintext — an attack that blocks the road to encryption before encryption starts.

HSTS (HTTP Strict Transport Security) blocks that road. Once a server issues the Strict-Transport-Security header, for the specified period the browser doesn’t even attempt HTTP to that site — it rewrites the address to HTTPS itself. The first HTTP response to tamper with ceases to exist. Going further, major sites are hardcoded into browsers as a preload list, forcing HTTPS from the very first visit.


3. Follow Along

3-1. Making a Self-Signed Certificate — "I Vouch for Myself"

Input (Linux/WSL):

mkdir -p tls-lab && cd tls-lab
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 1 -nodes -subj "/CN=lab.local"
openssl x509 -in cert.pem -noout -subject -issuer -dates

Output (measured 2026-09-09):

subject=CN = lab.local
issuer=CN = lab.local
notBefore=Sep  9 07:52:37 2026 GMT
notAfter=Sep 10 07:52:37 2026 GMT

How to read it: subject (whose certificate is it) and issuer (who vouched for it) are both lab.local — self-vouching, the decisive trait of self-signed. The validity period is one day (-days 1). key.pem is the private key paired with this certificate — don’t give it to anyone.

3-2. Running a Local TLS Server

Open a practice HTTPS server with the certificate you just made.

Input:

openssl s_server -accept 4433 -cert cert.pem -key key.pem -www &

How to read it: -accept 4433 means wait on port 4433, and -www is a practice mode that returns a simple status page on connection. Try visiting https://localhost:4433 in your browser — a warning like "this site can’t be trusted" appears. Because the issuer of the certificate you just made isn’t on the trust list. This warning is the scene of 2-1’s "impersonation prevention" at work.

3-3. Observing the Handshake — Through s_client’s Eyes

This time, connect with openssl’s client instead of a browser, and see the negotiation result as text.

Input (new terminal):

echo | openssl s_client -connect 127.0.0.1:4433 -servername lab.local

Output (measured 2026-09-09, partial):

depth=0 CN = lab.local
verify error:num=18:self-signed certificate
verify return:1
CONNECTED(00000003)
---
Certificate chain
 0 s:CN = lab.local
   i:CN = lab.local
   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
   v:NotBefore: Sep  9 07:52:37 2026 GMT; NotAfter: Sep 10 07:52:37 2026 GMT
---
SSL handshake has read 1337 bytes and written 391 bytes
Verification error: self-signed certificate
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384

How to read it: look at four places. ① verify error:num=18:self-signed certificate — verification failed, reason: "self-signed." Exactly 2-2’s explanation that the guarantor isn’t on the trust list. ② Confirm again that s: (subject) and i: (issuer) in the Certificate chain are the same. ③ read 1337 bytes and written 391 bytes — during the negotiation, the server sent 1337 bytes (mostly the certificate) and the client wrote 391. ④ New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 — the finally agreed version and cipher suite. The handshake didn’t fail — it completed, only in an "identity unverified" state. That point is the core. The fake certificate a MITM attacker presents lands in exactly this state.

Why: encryption was established, but identity verification failed. "Encrypted" and "identity verified" are separate things — the browser’s warning announces the latter’s failure.

3-4. Comparing with a Real Site’s Handshake

Let’s revisit the list from when you captured a public site in Step 85 (re-quoted from Step 85, measured 2026-09-09):

    4 ... 192.168.39.82 → 172.66.147.243 TLSv1   583 Client Hello (SNI=example.com)
    6 ... 172.66.147.243 → 192.168.39.82 TLSv1.3 3750 Server Hello, Change Cipher Spec
    7 ... 172.66.147.243 → 192.168.39.82 TLSv1.3 372  Application Data
   10 ... 192.168.39.82 → 172.66.147.243 TLSv1.3 146  Change Cipher Spec, Application Data

How to read it: the Client Hello’s SNI (who you’re connecting to) is visible in plaintext, but everything after is Application Data — ciphertext. Also, there’s no Certificate line in the list. That’s because in TLS 1.3, even the certificate moves inside the encrypted zone (confirmed in Step 85, 3-4). The negotiation you saw as text in today’s 3-3 looks like this in a packet capture.

3-5. Looking Deep Inside a Certificate

Let’s unfold the insides of the certificate made in 3-1 as text.

Input:

openssl x509 -in cert.pem -noout -text | head -20

Output (measured 2026-09-09, partial):

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            3b:cf:04:db:7a:86:00:83:4a:d8:a6:fc:f7:9b:69:da:ca:e1:5f:7b
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN = lab.local
        Validity
            Not Before: Sep  9 07:52:37 2026 GMT
            Not After : Sep 10 07:52:37 2026 GMT
        Subject: CN = lab.local
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)

How to read it: the certificate’s identity is visible — serial number, signature algorithm, guarantor (Issuer), validity period, owner (Subject), and the owner’s public key. 2-1’s definition — "certificate = identity + public key + guarantor’s signature" — is literally in there. The serial number is used to point at "certificate number such-and-such is revoked" when invalidating a certificate.

3-6. How to Read an HSTS Header

HSTS is a single response header from the server.

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

How to read it: max-age=31536000 is a declaration of "from now on, for 1 year (in seconds), this site is HTTPS only," and includeSubDomains means apply the same rule to subdomains. A browser that received this header will, even if the user types an address starting with http://, rewrite it to https:// itself before sending the request. The moment sslstrip’s target — "the first plaintext connection" — disappears.

Confirmation exercise: in your browser’s dev tools (F12) → Network tab, open any major site and look at the response headers. Whether this header is present, and how large its max-age is, is that site’s downgrade-defense level.


4. Missions & Exercises

Mission — TLS Defense-Structure Diagram and Handshake Interpretation

  1. Reproduce 3-1~3-3, and organize four values from the s_client output (verification result, issuer, bytes read, agreed cipher suite) into a table.
  2. Visit https://localhost:4433 in a browser, record the warning text, and connect it in one sentence to the attack it blocks (impersonation MITM).
  3. Attacker-perspective summary: write three paths by which MITM bypasses TLS (fake certificate + inducing the user to ignore the warning, HTTP downgrade, certificate theft), and pair each with its defense.
  4. On paper or a whiteboard app, draw — over the "query → response → connect" flow — the layer DNS spoofing breaks and the layers TLS/HSTS defend, and save it as step160_tls_hsts.png (or a text diagram in .md).

Exercises

Exercise 1. What does it mean that a self-signed certificate’s subject and issuer are the same, and why don’t browsers trust it?

Exercise 2. In 3-3, the handshake completed yet a "Verification error" appeared. Why does it matter for MITM defense that "encryption established" and "identity verified" are separate?

Exercise 3. sslstrip targeted not "encrypted communication" but "the moment of crossing into encryption." How does HSTS neutralize this attack?

Exercise 4. HSTS has a weakness too — "the moment the browser visits that site for the first time." How does the preload list compensate for this weakness?


Answers & completion criteria · expand/collapse

5. Model Answers & Completion Criteria

Mission Model Answer

Example table for item 1 (values measured 2026-09-09):

Item Value Meaning
Verification result verify error:num=18:self-signed certificate No trustworthy guarantor (CA)
issuer CN = lab.local Same as subject — self-signed
Bytes read 1337 Mostly the server certificate
Cipher suite TLSv1.3 / TLS_AES_256_GCM_SHA384 Negotiated version and cipher

Item 2: example warning text — "this site’s security certificate is not trusted" (varies per browser). This warning blocks impersonation MITM by exploiting the fact that "even if you fool DNS or ARP to lead someone to a fake server, that server cannot present the real domain’s certificate."

Example pairing for item 3: ① fake certificate → browser warning + user education (never ignore warnings), ② HTTP downgrade → HSTS (+ preload), ③ certificate private-key theft → key management and revocation (OCSP/CRL), regular reissuance.

How to verify: ① were the table’s four values transferred from actual output? ② did you interpret the warning text not as "an annoying popup" but as "an impersonation-detection signal"? ③ is each of the three bypass paths paired with the correct defense? ④ in the diagram, are DNS spoofing (the name→address layer) and TLS (the identity+content layer) drawn as different layers?

Exercise Answers

Answer 1. It means the issuer is itself — a declaration of "I vouch for myself." Browsers don’t trust it because there’s no pre-installed root CA at the end of its verification — anyone can make a self-signed certificate, so trusting one would let an impersonator’s certificate pass too.

Answer 2. Encryption makes "contents unreadable in the middle," but it doesn’t say who the other party is. Without identity verification, the victim ends up talking to the attacker, encrypted all the while. That’s why TLS bundles encryption and certificate verification as one set, and browsers expose verification failure as a warning, giving the human a chance to stop.

Answer 3. A browser that received the HSTS header sends no HTTP requests to that site at all during the specified period — it rewrites to HTTPS itself. The "first plaintext response" that MITM would tamper with ceases to exist, so the downgrade’s foothold itself disappears.

Answer 4. HSTS works only after the header is "received once," so if the first visit is plaintext, that one time is the attack window. The preload list closes that window by planting major sites’ HSTS policies into the browser itself in advance, making even the very first visit start as HTTPS.

Completion Criteria Checklist

  • [ ] I can explain the TLS handshake’s 4 stages in order
  • [ ] I can explain the certificate chain’s structure (site → intermediate → root)
  • [ ] I can make a self-signed certificate and confirm subject=issuer
  • [ ] I can read the verification result and cipher suite from s_client output
  • [ ] I can explain "encryption established ≠ identity verified"
  • [ ] I can explain sslstrip’s principle and HSTS’s counter
  • [ ] I know what information remains outside encryption, such as SNI

6. Common Pitfalls & Fixes

Wall 1. "s_server can’t open the port"

Symptom: using a low port like 443 instead of 4433 raises a Permission denied-type error.
Cause: ports 1024 and below require administrator privileges.
Fix: use a high port like 4433 for practice, or run with sudo.

Wall 2. "I ignored the browser warning and went in — is that safe?"

Symptom: you enter a self-signed site via "Advanced → Proceed."
Cause: if it’s a certificate you just made yourself, as in today’s practice, it’s fine — you can vouch for the identity yourself.
Fix: but never carry this habit out onto the real internet. Ignoring a certificate warning on an unknown site and proceeding is the final gate of real-world MITM. Leave it as a felt lesson: "warnings aren’t there to be ignored."

Wall 3. "s_client hangs for a long time"

Symptom: after connecting, there’s no further output and it just waits.
Cause: s_client is interactive — it’s connected and waiting for input.
Fix: type an HTTP request yourself (GET / HTTP/1.0 then Enter twice), or close input immediately like the earlier echo |. To exit, Q or Ctrl+C.

Wall 4. "I can’t see the certificate in plaintext"

Symptom: you can’t find the Certificate packet in a capture.
Cause: in TLS 1.3, the certificate moves inside the encrypted zone (3-4). Not an error — an improvement of the modern protocol.
Fix: if you want to see certificate contents, use 3-3’s s_client output (text), not a capture. There’s also a -tls1_2 option forcing TLS 1.2, but if your goal is "understanding the current standard," 1.3 as-is is right.

Wall 5. "I sent the HSTS header but http still connects"

Symptom: you attached the header to your practice server but the browser allows http connections.
Cause: HSTS works only "after connecting via HTTPS once and receiving the header." Some browsers don’t trust headers from sessions where the self-signed warning was bypassed.
Fix: verify in a proper-certificate environment. This behavior itself is a good observation showing HSTS’s limit — "the first-visit window."


7. Summary

Today’s Concepts

Concept One-line explanation
TLS The protocol putting HTTP inside an encrypted channel — content confidentiality + identity verification
Certificate An ID card signed by a CA: "this domain’s owner = this public key’s owner"
CA / chain The root of trust / a relay of guarantees running site → intermediate → root
Self-signed certificate A certificate whose issuer = subject — for learning; browsers warn
Handshake Version/cipher-suite negotiation + certificate exchange + session-key agreement
SNI The destination name inside Client Hello — information left outside encryption
sslstrip A downgrade attack swapping the first HTTPS-crossing moment to HTTP
HSTS A "this site is HTTPS only" declaration — removes the downgrade’s foothold
preload HSTS policies built into the browser — closes even the first-visit window

Today’s Commands

Command What it does
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 1 -nodes -subj "/CN=name" Generates a self-signed certificate and private key
openssl x509 -in cert.pem -noout -subject -issuer -dates Reads a certificate’s subject, issuer, and dates
openssl s_server -accept 4433 -cert cert.pem -key key.pem -www Practice TLS server
echo | openssl s_client -connect 127.0.0.1:4433 -servername name Observes the handshake and checks the verification result

An Instinct More Important Than Commands

The last chapter’s attacker could swap out the phone book (DNS). Yet the fake ID card you made yourself today got caught on a single num=18 line. Now you can see exactly where attack and defense fight — on the layer of names, the attack wins; on the layer of identity, the defense wins.

And the final variable is the human. However many warnings the browser raises, if the user clicks "proceed anyway," every layer collapses. Even when technology blocks, the human breaks through — remember the warning screen you made today as the starting point of "how not to make warnings ignorable" in every system you’ll design going forward.


Once every box is checked, Step 160 is complete.

ONE STEP FURTHER

Finished this lesson?

Check the completion criteria, then mark your progress.

Something wrong with this page or a link? Let us know.
Next