HTTPS, Zertifikate und Sicherheits-Header · Übung
Ein Kontext, der verifiziert
In Python leben die drei Prüfungen der letzten Lektion in einem ssl, einem Kontext. Ein Kontext ist ein Bündel von tls_contexts.py.
Der Kontext des Clients
Ein Aufruf baut einen Client-Kontext mit sicheren Einstellungen:
import ssl
context = ssl.create_default_context(cafile="certs/ca.pem")
print(context.verify_mode)
print(context.check_hostname)
print(context.minimum_version)
cafile ist die certs/ca.pem enthält einen Testaussteller, der für diesen Kurs erstellt wurde. Die Datei certs/README.md beschreibt ihn.
Drei Einstellungen des Kontexts entsprechen dem, was du in Lektion 1 gelernt hast:
verify_modeistssl.CERT_REQUIRED. Der Server muss ein Zertifikat vorlegen, und der Kontext prüft dessen Aussteller und Daten.check_hostnameistTrue. Der Kontext vergleicht die Namen des Zertifikats mit dem Namen, nach dem der Client gefragt hat.minimum_versionsagt, welche TLS-Versionen der Client akzeptiert. Versionen vor TLS 1.2 haben bekannte Schwächen, also setzt deinessl.TLSVersion.TLSv1_2und verlässt sich nicht auf den Standardwert.
Ein Kontext prüft von sich aus nichts. Die Prüfungen passieren im Handshake, wenn ein Client einen verbundenen
secure = context.wrap_socket(sock, server_hostname="reading.example")
Der Name in server_hostname ist der, den die Namensprüfung mit dem Zertifikat vergleicht. Wenn eine der drei Prüfungen fehlschlägt, löst dieser Aufruf einen Fehler aus, und der Client bekommt nie eine Verbindung, auf der er eine Anfrage senden könnte. Das siehst du in der nächsten Lektion passieren. In dieser baust du nur die Kontexte, und zwar so, dass alle drei Prüfungen eingeschaltet sind.
Drei Arten, sie auszuschalten
Die Verifizierung steht im Weg, wenn ein Zertifikat falsch ist, und auf einem Testserver ist ein Zertifikat oft falsch. Also schalten Leute die Verifizierung aus, meist mit einer nahen Deadline und dem Plan, sie wieder einzuschalten. Dann bleibt die Zeile. Du solltest sie in einem Review finden können. Sie hat drei Formen.
Die erste ist context.check_hostname = False. Der Aussteller wird weiterhin geprüft, der Name nicht. Die zweite ist context.verify_mode = ssl.CERT_NONE, womit das Zertifikat gar nicht mehr geprüft wird. Die dritte ist ein Kontext, der mit einem bloßen ssl.SSLContext() gebaut wird, der mit beiden Prüfungen ausgeschaltet beginnt.
Bibliotheken haben denselben Schalter unter anderem Namen. In requests und httpx ist es verify=False.
Mit ausgeschalteter Verifizierung verschlüsselt TLS weiterhin. Aber der Client weiß nicht mehr, wer das andere Ende hält, also verschlüsselt er sein Passwort für den, der geantwortet hat. Das ist der ganze Angriff: Jemand auf dem Weg antwortet anstelle des Servers, und der Client merkt es nicht.
Vier Fragmente bauen jeweils einen Kontext. Welches prüft noch sowohl den Aussteller als auch den Namen?
# 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")
Ein Kontext hat check_hostname ausgeschaltet und verlangt weiterhin ein Zertifikat. Wer kann als reading.example antworten?
Was passiert in der zweiten Zeile?
context = ssl.create_default_context()
context.verify_mode = ssl.CERT_NONE
Der Kontext des Servers
Auch ein Server braucht einen Kontext, für die andere Hälfte des Handshakes. Er legt ein Zertifikat vor und beweist, dass er den Schlüssel besitzt:
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(certfile, keyfile)
ssl.PROTOCOL_TLS_SERVER erstellt einen Kontext für die Serverseite. load_cert_chain lädt das Zertifikat und seinen privaten Schlüssel. Ein Server-Kontext verifiziert niemanden. Ein Browser hat meist kein Zertifikat vorzuzeigen, und Zertifikate für Clients liegen außerhalb dieses Kurses.
Die Schlüsseldatei ist das Geheimnis des Servers, wie der Signaturschlüssel aus Kapitel 5. Ein echter Schlüssel liegt nie in einem Projektordner. Die Schlüssel in certs/ sind Testschlüssel, die jeder lesen kann, und deshalb hat jeder von ihnen TEST-ONLY im Namen.
Deine Aufgabe
Öffne tls_contexts.py. Die Datei enthält drei Funktionen, die noch nicht geschrieben sind. Schreib jetzt zwei davon:
client_context(cafile)gibtssl.create_default_context(cafile=cafile)zurück, mitminimum_versionaufssl.TLSVersion.TLSv1_2gesetzt. Er vertraut nur der Datei, die er bekommen hat.server_context(certfile, keyfile)gibt einen mitssl.PROTOCOL_TLS_SERVERerstellten Kontext zurück, mit derselben Mindestversion, der genau dieses Zertifikat und diesen Schlüssel geladen hat.
Lass tls_failure für die nächste Lektion. Dann führ die Datei aus:
python tls_contexts.py
Sie baut deinen Client-Kontext aus certs/ca.pem und gibt vier seiner Einstellungen aus:
verify_mode: CERT_REQUIRED
check_hostname: True
minimum_version: TLSv1_2
authorities: 1
Dann drück Check my work. Die Prüfung liest die Einstellungen deiner Kontexte und verbindet sich nirgendwohin. Einstellungen, die auf dem Papier stimmen, sind noch kein Handshake. In der nächsten Lektion siehst du zu, wie sich deine beiden Kontexte begegnen.
Aufgabe
Schreib die beiden Kontextfunktionen in tls_contexts.py.
client_context(cafile)gibt einen Client-Kontext zurück, der nur den Zertifizierungsstellen in der Dateicafilevertraut. Bau ihn mitssl.create_default_context(cafile=cafile)und setzminimum_versionaufssl.TLSVersion.TLSv1_2. Lassverify_modeundcheck_hostnameso, wie dieser Aufruf sie setzt. Lade nicht dieder Zertifizierungsstellen des Systems. server_context(certfile, keyfile)gibt einen Server-Kontext zurück:ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)mitminimum_versionaufssl.TLSVersion.TLSv1_2gesetzt, nachdemload_cert_chain(certfile, keyfile)genau das Paar geladen hat, das er bekommen hat.
Lass tls_failure für die nächste Lektion.
Führ python tls_contexts.py im verify_mode: CERT_REQUIRED, check_hostname: True, minimum_version: TLSv1_2 und authorities: 1 aus.
Dann drück Check my work. Die Prüfung ruft client_context mit einer Datei von Zertifizierungsstellen auf, die du nicht gesehen hast, und liest die Einstellungen des Kontexts und die Zertifizierungsstellen, denen er vertraut. Sie ruft server_context mit einem anderen certs/ auf und prüft, welches Paar geladen wurde. Sie macht keinen Handshake und öffnet keine Verbindung. Nichts aus deinem Terminal wird bewertet.