0%

HTTPS, Certificates, and Security Headers · practice

A Context That Verifies

In Python, the three checks of the last lesson live in an from the ssl , called a context. A context is a bundle of settings. You make it once and use it for many connections. with Python showed a context being used. In this lesson you make two: one for a client and one for a server. Both go into a new file in your project, tls_contexts.py.

The client’s context

One call builds a client context with safe settings:

import ssl

context = ssl.create_default_context(cafile="certs/ca.pem")
print(context.verify_mode)
print(context.check_hostname)
print(context.minimum_version)

cafile is the client’s of trusted issuers, as a file of . When you pass a file, the context trusts the issuers in that file and no others. When you pass nothing, it loads the list that came with the . This chapter always passes a file: certs/ca.pem holds one test issuer, made for this course. The file certs/README.md describes it.

Three settings on the context match what you learned in Lesson 1:

  • verify_mode is ssl.CERT_REQUIRED. The server must present a certificate, and the context checks its issuer and its dates.

  • check_hostname is True. The context compares the certificate’s names with the name the client asked for.

  • minimum_version says which TLS versions the client accepts. Versions before TLS 1.2 have known weaknesses, so your sets ssl.TLSVersion.TLSv1_2 and does not rely on the default.

A context checks nothing by itself. The checks happen in the handshake, when a client wraps a connected with the context and names the host it expects:

secure = context.wrap_socket(sock, server_hostname="reading.example")

The name in server_hostname is the one the name check compares with the certificate. If any of the three checks fails, this call raises an error and the client never gets a connection to send a request on. You will see that happen in the next lesson. In this one you only build the contexts, and you build them so that all three checks are on.

Three ways to switch it off

Verification gets in the way when a certificate is wrong, and a certificate is often wrong on a test server. So people switch verification off, usually with a deadline close and a plan to switch it back on. Then the line stays. You should be able to find it in a review. It has three forms.

The first is context.check_hostname = False. The issuer is still checked, and the name is not. The second is context.verify_mode = ssl.CERT_NONE, which stops checking the certificate at all. The third is a context built with a bare ssl.SSLContext(), which starts with both checks off.

Libraries have the same switch under another name. In requests and httpx it is verify=False.

With verification off, TLS still encrypts. But the client no longer knows who holds the other end, so it encrypts its password for whoever answered. That is the whole attack: someone on the path answers in place of the server, and the client does not notice.

Four fragments each build a context. Which one still checks both the issuer and the name?

# A
context = ssl.create_default_context()
context.check_hostname = False

# B
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE

# C
context = ssl.SSLContext()

# D
context = ssl.create_default_context(cafile="certs/ca.pem")

A context has check_hostname off and still requires a certificate. Who can answer as reading.example?

What happens at the second line?

context = ssl.create_default_context()
context.verify_mode = ssl.CERT_NONE

The server’s context

A server needs a context too, for the other half of the handshake. It presents a certificate and proves that it holds the key:

context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(certfile, keyfile)

ssl.PROTOCOL_TLS_SERVER makes a context for the server side. load_cert_chain loads the certificate and its private key. A server context does not verify anyone. A browser usually has no certificate to show, and certificates for clients are outside this course.

The key file is the server’s secret, like the signing key of Chapter 5. A real key never sits in a project folder. The keys in certs/ are test keys that anyone can read, which is why every one of them has TEST-ONLY in its name.

Your task

Open tls_contexts.py. It holds three functions that are not written yet. Write two of them now:

  • client_context(cafile) returns ssl.create_default_context(cafile=cafile) with minimum_version set to ssl.TLSVersion.TLSv1_2. It trusts only the file it was given.

  • server_context(certfile, keyfile) returns a context made with ssl.PROTOCOL_TLS_SERVER, with the same minimum version, that has loaded exactly that certificate and key.

Leave tls_failure for the next lesson. Then run the file:

python tls_contexts.py

It builds your client context from certs/ca.pem and prints four of its settings:

verify_mode: CERT_REQUIRED
check_hostname: True
minimum_version: TLSv1_2
authorities: 1

Then press Check my work. It reads the settings of your contexts and never connects anywhere. Settings that are right on paper are not yet a handshake. In the next lesson you watch your two contexts meet.

Task

Write the two context in tls_contexts.py.

  • client_context(cafile) returns a client context that trusts only the authorities in the file cafile. Build it with ssl.create_default_context(cafile=cafile) and set minimum_version to ssl.TLSVersion.TLSv1_2. Leave verify_mode and check_hostname as that call sets them. Do not load the system’s of authorities.

  • server_context(certfile, keyfile) returns a server context: ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) with minimum_version set to ssl.TLSVersion.TLSv1_2, after load_cert_chain(certfile, keyfile) loaded exactly the pair it was given.

Leave tls_failure for the next lesson.

Run python tls_contexts.py in the . It prints verify_mode: CERT_REQUIRED, check_hostname: True, minimum_version: TLSv1_2, and authorities: 1.

Then press Check my work. It calls client_context with an authority file you have not seen and reads the context’s settings and the authorities it trusts. It calls server_context with another and key from certs/ and checks which pair was loaded. It performs no handshake and opens no connection. Nothing from your terminal is graded.