HTTPS and the TLS Handshake
What TLS protects, how the TLS 1.3 handshake agrees on keys in one round trip, how certificates prove a server's identity, and what goes wrong when verification is switched off. Tested locally.
HTTPS is HTTP sent over a TLS-encrypted connection, which gives confidentiality, integrity and authentication of the server. In the TLS 1.3 handshake, client and server exchange Diffie-Hellman key shares, the server proves its identity with a certificate and signature, and both send Finished messages. Interviewers expect that TLS 1.3 needs one round trip, against two in TLS 1.2.
HTTPS is HTTP sent through a TLS-encrypted connection. TLS gives three things: confidentiality (nobody can read the data), integrity (nobody can change it unnoticed) and authentication (you are talking to the real server). In TLS 1.3 the handshake takes one round trip.
- TLS gives three guarantees: data cannot be read in transit, it cannot be changed without detection, and the server is the owner of the requested domain name.
- The TLS 1.3 handshake completes in one network round trip, while TLS 1.2 needed two, and TLS 1.3 removed RSA key exchange and CBC-mode ciphers.
- TLS uses asymmetric cryptography only during the handshake to authenticate the server and agree a secret, then encrypts data with a fast symmetric cipher such as AES-GCM or ChaCha20-Poly1305.
- A TLS client checks that the certificate chain leads to a trusted root CA, the dates are valid, the certificate is not revoked, and the hostname matches.
- Forward secrecy, mandatory in TLS 1.3, comes from ephemeral Diffie-Hellman keys, so stealing a server's private key later does not decrypt recorded past traffic.
- Disabling certificate verification, such as requests.get(url, verify=False) in Python, keeps TLS encryption but loses authentication, so an attacker on the path can intercept.
01What TLS promises
Plain HTTP sends everything as readable text. Anyone on the path, such as a shared Wi-Fi, an ISP or a compromised router, can read passwords and cookies or alter the page. TLS (Transport Layer Security, the successor of SSL) wraps the connection to stop that.
It guarantees that data cannot be read in transit, that it cannot be changed without detection, and that the server you reached is the owner of the domain name you asked for. It does not tell you the site is honest. A phishing site can have a perfectly valid certificate for its own domain. The padlock means "the connection is private and goes to this domain", nothing more.
02Symmetric and asymmetric: why both
Symmetric encryption (AES, ChaCha20) uses one shared secret key and is fast, but both sides need the key first. Asymmetric encryption uses a public/private pair, so two strangers can set up a secret over an open network, but it is slow.
TLS uses the slow part only for the handshake, to authenticate the server and agree a shared secret. After that, all the data is encrypted with a fast symmetric cipher using keys derived from that secret.
03The TLS 1.3 handshake step by step
- The client sends a ClientHello with the versions and cipher suites it supports and its key share, the public half of a fresh Diffie-Hellman key (usually elliptic-curve, X25519).
- The server answers with a ServerHello choosing a cipher and sending its own key share. From the two key shares both sides can compute the same shared secret, and from here on the handshake itself is encrypted.
- The server sends its certificate, and a signature over the handshake made with its private key (CertificateVerify), proving it holds the key that matches the certificate.
- Both sides send a Finished message, a MAC over the whole handshake, which detects any tampering.
- The client can send the HTTP request right away.
That is one round trip. TLS 1.2 needed two. TLS 1.3 also removed old, weak options such as RSA key exchange and CBC-mode ciphers.
04Seeing it work locally
I generated a self-signed certificate for localhost, started a TLS server on it, and connected with a client that trusts that certificate:
import socket, ssl
# assumes c.pem is the self-signed certificate and a TLS server using it listens on `port`
ctx = ssl.create_default_context(cafile='c.pem') # trust our own certificate
with socket.create_connection(('127.0.0.1', port)) as raw:
with ctx.wrap_socket(raw, server_hostname='localhost') as tls:
print(tls.version(), tls.cipher())
print(tls.getpeercert()['subject'])In my run this printed TLSv1.3, the cipher ('TLS_AES_256_GCM_SHA384', 'TLSv1.3', 256), and the subject commonName: localhost. The numbers will differ across Python and OpenSSL versions, but a modern setup negotiates TLS 1.3 with an AEAD cipher such as AES-GCM or ChaCha20-Poly1305.
05Certificates and the chain of trust
A certificate holds the domain names (in the Subject Alternative Name field), the server's public key, a validity period, and a signature from an issuer, a certificate authority (CA). Your OS or browser ships with a list of root CAs it trusts. The server usually sends its certificate plus any intermediate CA certificates, and the client follows the signatures up the chain to a trusted root.
The client checks four things: the chain leads to a trusted root, the dates are valid, the certificate has not been revoked, and the hostname matches. That last one is easy to forget. Here the same server is contacted while claiming the name example.com:
ctx.wrap_socket(raw, server_hostname='example.com') # same ctx and server as above
# ssl.SSLCertVerificationError: certificate verify failed: Hostname mismatchThe handshake completes cryptographically, but the certificate is for localhost, so verification fails. Without a hostname check, an attacker with any valid certificate for any domain could impersonate every other site.
06Forward secrecy and what an attacker sees
Because each connection uses a fresh, ephemeral Diffie-Hellman key, the session keys are never sent and are not derived from the server's long-term key. If an attacker records encrypted traffic today and steals the server's private key next year, they still cannot decrypt the recording. This is forward secrecy, and TLS 1.3 makes it mandatory.
An eavesdropper still sees the server's IP address, the amount and timing of the data, and in many cases the hostname, since the Server Name Indication in the ClientHello has traditionally been sent in clear text. A newer extension, Encrypted Client Hello (standardised as RFC 9849 in 2026), hides it where both the client and the server's provider support it.
HSTS is a response header that tells the browser to use HTTPS for that site from now on, which blocks the downgrade where an attacker strips the s from the first request.
07Common mistakes and interview questions
The classic mistake is disabling certificate checks to make an error disappear: requests.get(url, verify=False) in Python, or rejectUnauthorized: false in Node. It keeps the encryption but throws away the authentication, so any attacker on the path can sit in the middle. Fix the trust problem instead, by installing the right CA certificate or correcting the hostname.
Q: What is the difference between SSL and TLS?
A: TLS is the standardised successor of SSL. SSL 2.0 and 3.0 are broken and should not be used. People still say "SSL certificate" out of habit.
Q: Why does TLS use both symmetric and asymmetric cryptography?
A: Asymmetric for authentication and agreeing a secret, symmetric for the bulk data because it is far faster.
Q: What is forward secrecy?
A: Session keys come from ephemeral key exchanges, so compromising long-term keys later does not expose past sessions.
Q: How does a client trust a certificate?
A: It verifies the signature chain up to a root CA in its trust store, checks the dates and revocation, and confirms the hostname is in the certificate.
Q: What does 1-RTT mean for TLS 1.3?
A: The handshake completes after one network round trip, against two in TLS 1.2.