0%

Eén verbinding, veel verzoeken

HTTPS is HTTP binnen TLS

Elke byte in deze cursus was leesbaar op de lijn. In hoofdstuk 7 stuurde je client een wachtwoord in een authorization-header als , wat iedereen die het ziet kan terugdraaien; je server deelde sessie-id’s uit in ; schrijfverzoeken droegen bearer-tokens. Dat was alleen veilig omdat al die bytes binnen één machine bleven, op het . Over een echt netwerk verstuurd zou elk ervan gelezen en gekopieerd kunnen worden door iedereen op de route: de wifi, de internetprovider, een proxy ertussen. HTTPS is hoe het web dat voorkomt, en het verandert minder dan je misschien verwacht.

Dezelfde bytes, ingepakt

HTTPS is verstuurd via , de versleutelingslaag die je in Networks with Python hebt leren kennen. TLS zit tussen de en de HTTP-bytes. In het schema hieronder staan links en rechts dezelfde lagen: HTTP bytes zijn de HTTP-bytes, TLS versleutelt en controleert ze, en daaronder zit de TCP-verbinding naar 80 of 443:

http://books.example/books          https://books.example/books

HTTP bytes                          the same HTTP bytes
                                    TLS: encrypts and checks them
TCP connection to port 80           TCP connection to port 443

Een https- betekent drie dingen: maak verbinding met poort 443, tenzij de URL een andere noemt, voer vóór al het andere een TLS-handshake uit, en stuur daarna precies de HTTP die je de hele cursus hebt geschreven, door de versleutelde laag. De verzoekregel, de headers, content-length, chunks, keep-alive en Connection: close blijven allemaal gelijk. TLS weet niet en maakt het niet uit dat de bytes HTTP zijn.

Wat TLS toevoegt

TLS geeft een verbinding drie dingen:

  • Versleuteling. Alles erbinnen, het pad en de query, elke header inclusief cookie en authorization, en elke body, reist als bytes die alleen de twee uiteinden kunnen lezen.

  • Integriteit. Als iets op de route een byte verandert, merkt de ontvangende kant dat en mislukt de verbinding, in plaats van gewijzigde data door te geven.

  • Identiteit van de server. De server toont een certificaat voor zijn naam, ondertekend door een autoriteit die de client vertrouwt, en de client controleert dat de naam overeenkomt met de host in de URL. Zonder die controle zou een client een perfect versleutelde verbinding kunnen opzetten met een bedrieger.

Sommige dingen blijven zichtbaar. Een waarnemer ziet nog steeds de adressen en poorten, wanneer de verbinding opent en sluit, en ongeveer hoeveel bytes er in elke richting gaan. Meestal ziet hij ook de naam van de server, omdat de client die vroeg in de handshake noemt, zodat de server het juiste certificaat kan kiezen.

Iemand bekijkt een HTTPS-verzoek voor /me dat een sessiecookie bevat. Wat kan diegene lezen?

Basic-inloggegevens worden via HTTPS verstuurd. Wat verandert er?

In Python

De ssl uit de standaardbibliotheek pakt een verbonden in. Deze regels zijn om te lezen, niet om hier uit te voeren:

import socket
import ssl

host = "books.example"
context = ssl.create_default_context()
with socket.create_connection((host, 443), timeout=5) as sock:
    with context.wrap_socket(sock, server_hostname=host) as tls:
        tls.sendall(b"GET /books HTTP/1.1\r\nHost: books.example\r\nConnection: close\r\n\r\n")
        print(tls.recv(4096))

ssl.create_default_context() controleert standaard het certificaat en de hostnaam. http.client.HTTPSConnection(host, timeout=5), de https-tegenhanger van de klasse die je sinds hoofdstuk 3 gebruikt, doet dit inpakken voor je. server_hostname is de naam die gecontroleerd wordt, en de naam die de client in de handshake noemt. Het dat wrap_socket teruggeeft, heeft sendall en recv, dus read_head, read_chunked en read_body zouden er precies zo uit lezen als uit een gewone socket.

Met requests is een https-URL alles wat nodig is: requests.get("https://books.example/books", timeout=5) voert de handshake uit en controleert het certificaat, omdat de instelling verify standaard aan staat. Hoofdstuk 10 komt erop terug. Als je verify uitzet, blijft de versleuteling, maar gooi je de identiteit van de server weg.

Waarom niet hier

Deze site heeft geen TLS-endpoint. De oefenservice en je eigen server spreken gewone HTTP, en lessandboxen bereiken verder niets, dus geen enkele les hier kan een handshake of een certificaat laten zien.

Als je Python op je eigen computer hebt en een internetverbinding, kun je een echte bekijken. Deze optionele stap hoort niet bij de cursus, en niets controleert hem. Hij drukt de TLS-versie af en het subject van het certificaat van een site die jij kiest. Het blok is voor een -shell zoals bash of zsh; op Windows sla je de Python-regels tussen de EOF-markeringen op in een bestand en voer je dat uit met python.

python3 - <<'EOF'
import socket
import ssl

host = "www.python.org"  # or any https site you choose
context = ssl.create_default_context()
with socket.create_connection((host, 443), timeout=5) as sock:
    with context.wrap_socket(sock, server_hostname=host) as tls:
        print(tls.version())
        print(tls.getpeercert()["subject"])
EOF

HTTP/2 en HTTP/3

Veel sites beantwoorden https met HTTP/2. Dat behoudt , en headers, en wat ze betekenen; wat het verandert, is hoe berichten over de verbinding reizen. In plaats van tekstregels stuurt het binaire frames, en het vlecht veel verzoeken en antwoorden tegelijk door elkaar op één verbinding, zodat geen enkel verzoek wacht op het vorige, zoals de jouwe deden in fetch_many. Het comprimeert headers, en het schrijft elke headernaam in kleine letters, zoals je responsedicts al die tijd hebben gedaan. HTTP/3 vervoert dezelfde berichten over QUIC, dat op draait in plaats van TCP en TLS ingebouwd heeft.

Wat verandert HTTP/2?

Een clientbibliotheek kiest de versie voor je, en alles wat deze cursus heeft geleerd, van methodes en statuscodes tot caching, cookies en authenticatie, betekent in alle drie hetzelfde. Hoofdstuk 10 pakt requests en httpx op, nu je weet wat elk ervan voor je doet.