HTTPS, Zertifikate und Sicherheits-Header · Übung
Sieh zu, wie die Verifizierung gelingt und scheitert
Deine beiden Kontexte haben die richtigen Einstellungen, und noch keine Verbindung hat sie benutzt. In dieser Lektion siehst du ihnen bei der Arbeit zu: vier Handshakes auf diesem Computer, einer, der gelingt, und drei, die der Client ablehnt. Dann schreibst du die
Lies zuerst certs/README.md in deinem Projekt. Die Datei beschreibt die Dateien in certs/. Eine davon, ca.pem, ist eine Test-Zertifizierungsstelle: ein Aussteller, der für diesen Kurs erstellt wurde. Kein Browser und kein
Behalte während der ganzen Lektion eins im Kopf. Diese Handshakes zeigen, wie ein Client entscheidet. Über eine echte Website zeigen sie nichts.
Führ es aus
python pl_sec.py tls
Das Hilfsprogramm startet viermal einen kleinen Listener auf diesem Computer, jedes Mal mit deinem server_context() und einem anderen Zertifikat. Jedes Mal verbindet es sich mit deinem client_context("certs/ca.pem") und fragt nach dem Namen reading.example:
pl_sec: tls, four handshakes on 127.0.0.1 with your client_context() and server_context()
The name reading.example exists only in the model. Every byte goes to this computer.
--- the client ---
trusts certs/ca.pem only, and expects the name reading.example
--- the server presents certs/reading.pem ---
handshake completed (TLSv1.3); the server said: hello over TLS
--- the server presents certs/unknown-issuer.pem ---
refused: verify_code 20: unable to get local issuer certificate
--- the server presents certs/wrong-name.pem ---
refused: verify_code 62: Hostname mismatch, certificate is not valid for 'reading.example'.
--- the server presents certs/expired.pem ---
refused: verify_code 10: certificate has expired
--- result ---
1 handshake completed and 3 were refused, each before any request was sent
pl_sec: every thread has stopped
pl_sec: tls_failure() is not written yet (Write this function in Lesson 3.), so the failed checks have no name yet
Der Client verbindet seinen 127.0.0.1 und erwartet reading.example. Das sind zwei verschiedene Dinge. Ein Client prüft den Namen, nach dem er gefragt hat, nicht die Adresse, mit der er sich verbunden hat, und hier wird der Name nirgends nachgeschlagen: Das Hilfsprogramm übergibt ihn direkt an den Handshake. Ein Zertifikat kann auch Adressen auflisten. Das gute Zertifikat des Kurses listet 127.0.0.1 neben seinen Namen.
Sieh dir den ersten Block an. Der Handshake wurde abgeschlossen, und erst dann hat der Server etwas gesagt: hello over TLS ist innerhalb der verschlüsselten Verbindung gereist. Die Zeile nennt außerdem die Protokollversion, auf die sich die beiden Kontexte geeinigt haben. Deine beiden erlauben
Die drei Ablehnungen sind die drei Prüfungen aus Lektion 1. unknown-issuer.pem nennt reading.example, und seine Daten sind in Ordnung, aber sein Aussteller steht nicht in certs/ca.pem. wrong-name.pem kommt vom richtigen Aussteller, für other.example. expired.pem endete 2020. In allen drei Fällen hat der Client während des Handshakes aufgehört, also hat der Server am anderen Ende nie eine Anfrage erhalten.
Der Client hat expired.pem abgelehnt. Wie viel von einer HTTP-Anfrage hatte den Server bis dahin erreicht?
Der Fehler ist ein Objekt
Wenn eine Prüfung fehlschlägt, löst der Handshake ssl.SSLCertVerificationError aus. Die
verify_codeist eine Zahl von OpenSSL, der Bibliothek, die die Prüfung erledigt.20,62und10im Lauf oben sind Verify-Codes.verify_messageist ein Satz für Menschen.
Code, der die Verbindung herstellt, fängt die Ausnahme ab und liest sie, wie jedes Attribut einer Ausnahme:
try:
secure = context.wrap_socket(sock, server_hostname="reading.example")
except ssl.SSLCertVerificationError as error:
print(error.verify_code, error.verify_message)
Entscheide anhand des Codes und nie anhand der Meldung. Die Meldung formuliert OpenSSL, und die Formulierung ändert sich zwischen Versionen. Die Zahlen stammen aus OpenSSLs eigener Fehlerliste.
Python hat keine Namen für diese Zahlen, also liefert tls_contexts.py sie als drei Sets, mit einem Kommentar zu jeder Zahl: ISSUER_CODES, NAME_CODES und DATE_CODES. Die Sets enthalten mehr Codes, als der Lauf gezeigt hat. Code 18 zum Beispiel ist ein Zertifikat, das sein eigener Inhaber signiert hat. Dem begegnest du auf einem Entwicklungsrechner, wo Leute sich solche Zertifikate selbst erstellen. Code 9 ist ein Zertifikat, dessen Zeitraum noch nicht begonnen hat. Es fällt bei derselben Prüfung durch wie ein abgelaufenes, und deshalb heißt diese Prüfung hier Datumsprüfung und nicht „abgelaufen“.
unknown-issuer.pem nennt reading.example, und seine Daten sind in Ordnung. Warum wird es abgelehnt?
Der Server legt certs/wrong-name.pem vor. Was gibt tls_failure() für den Fehler zurück?
Deine Aufgabe
Schreib tls_failure(error) in tls_contexts.py. Die Funktion liest error.verify_code und gibt einen von vier
"issuer", wenn der Code inISSUER_CODESsteht;"name", wenn er inNAME_CODESsteht;"date", wenn er inDATE_CODESsteht;"other"für jeden anderen Code.
Dann führ das Szenario noch einmal aus:
python pl_sec.py tls
Jede Ablehnung hat jetzt eine Zeile mehr darunter, und die letzte Zeile des ersten Laufs ist verschwunden:
refused: verify_code 20: unable to get local issuer certificate
tls_failure() says: issuer
Die anderen beiden sagen tls_failure() says: name und tls_failure() says: date.
Was haben diese vier Handshakes über eine echte Website gezeigt?
Dann drück Check my work. Die Prüfung ruft deine Funktion mit Fehlerobjekten auf, die sie selbst erstellt, für die Codes, die du gesehen hast, und für Codes, die du nicht gesehen hast, also braucht sie keinen Handshake. Die Verbindung hat jetzt ein verifiziertes anderes Ende. Die nächste Lektion wendet sich der Seite zu, die darüber ankommt.
Aufgabe
Schreib tls_failure(error) in tls_contexts.py.
errorist einssl.SSLCertVerificationError. Lies seinenverify_code.Gib
"issuer"zurück, wenn der Code inISSUER_CODESsteht,"name", wenn er inNAME_CODESsteht,"date", wenn er inDATE_CODESsteht, und"other"für jeden anderen Code.Entscheide anhand von
verify_code.verify_messageist ein Satz für Menschen: Seine Formulierung kann sich zwischen OpenSSL-Versionen ändern, und er kann leer sein.
Führ python pl_sec.py tls im tls_failure() says: issuer, name oder date.
Dann drück Check my work. Die Prüfung ruft tls_failure mit Fehlerobjekten auf, die sie selbst erstellt: den drei Codes, die der Lauf zeigt, sechs, die er nicht zeigt, und einem, dessen Meldung leer ist. Sie macht keinen Handshake und öffnet keine Verbindung. Nichts aus deinem Terminal wird bewertet.