Sicherheitsanforderungen in Tests umsetzen

PythonBeginner
Jetzt üben

Einführung

Eine Korrektur kann bei einer späteren Änderung versehentlich rückgängig gemacht werden. Ein Sicherheitsregressionstest macht aus einer wichtigen Anforderung eine wiederholbare Anfrage mit einem erwarteten Ergebnis.

Du erweiterst eine kleine Python-Vorlage für einen geschützten DeskLite-Endpunkt. Anschließend prüfst du die Tests selbst anhand zweier absichtlich fehlerhafter lokaler Dienste. Weder die Installation eines Testframeworks noch Erfahrung im Schreiben von Tests ist nötig.

Den geschützten Endpunkt aufrufen

In diesem Schritt ermittelst du das Verhalten, das die Tests schützen müssen.

Der vorbereitete korrekte Dienst läuft auf dem lokalen Port 8080. Sein Endpunkt /tickets liefert bei einem gültigen Token das ursprüngliche Ticket und bei einem fehlenden oder falschen Token 401 mit einem Fehlerobjekt zurück. Authentifizierung und Geschäftsantwort sind getrennte Anforderungen: Der Status 200 allein beweist nicht, dass das richtige Ticket zurückgegeben wurde.

cd /home/labex/project/ticket-tests

Das in diesem Lab sichtbare course_test_fixture_token ist ein öffentliches, bewusst unechtes Testtoken. Es wird nur von diesen isolierten Lehrdiensten akzeptiert und besitzt außerhalb davon keine Zugriffsberechtigung. Es ersetzt nicht die zuvor behandelten privaten Anwendungszugangsdaten.

Verwende dieselbe Option für Anfrageheader von curl, -H, wie im Token-Lab:

curl -i -H 'Authorization: Bearer course_test_fixture_token' http://127.0.0.1:8080/tickets

Erwartet werden HTTP 200 und {"tickets": [{"id": 1, "title": "Printer offline"}]}. -i nimmt die HTTP-Antwortheader einschließlich des Status in die Ausgabe auf.

Lasse den Header nun vollständig weg:

curl -i http://127.0.0.1:8080/tickets

Erwartet werden HTTP 401 und {"error": "unauthorized"}. Diese Ablehnung ist das erforderliche Verhalten, kein Defekt der Übung.

Die kleine Testvorlage lesen

In diesem Schritt ordnest du jede Testzeile einem Teil einer echten Anfrage zu.

cat test_security.py

Die vorbereitete Datei enthält:

from api import request

def test_valid_token():
    response = request("/tickets", {"Authorization": "Bearer course_test_fixture_token"})
    assert response["status"] == 200
    assert response["body"] == {"tickets": [{"id": 1, "title": "Printer offline"}]}

from api import request lädt ein vorbereitetes Anfragehilfsprogramm. def test_valid_token(): definiert eine benannte Testfunktion. Die eingerückten Zeilen gehören zu dieser Funktion, genau wie bei der Suchfunktion aus dem vorherigen Lab.

request(path, headers) sendet eine echte HTTP-Anfrage und gibt ein Python-Dictionary zurück: eine Zuordnung von Namen zu Werten. Auch Headerzuordnungen verwenden geschweifte Klammern. {"Authorization": "Bearer ..."} sendet einen Header, während {} keinen sendet. response["status"] liest den Statuseintrag; response["body"] liest den ausgewerteten JSON-Antwortinhalt.

Eine Assertion formuliert eine Erwartung. Python vergleicht mit == zwei Werte; das unterscheidet sich vom einzelnen =, mit dem ein Wert zugewiesen wird. Ein falsches assert löst AssertionError aus und lässt den Test fehlschlagen. Die letzte Zeile vergleicht das vollständige erwartete Geschäftsobjekt einschließlich des Tickettitels.

Das Hilfsprogramm wählt anhand des Zielarguments des Teststarters einen vorbereiteten Dienst aus. Du bearbeitest die Tests, nicht das Hilfsprogramm oder den Dienst.

Die Lücke in einem bestandenen Test erkennen

In diesem Schritt siehst du, warum ein bestandener positiver Test nicht ausreicht.

Führe den vorbereiteten Teststarter aus. Sein Argument good wählt den korrekten Dienst aus:

python3 run_tests.py good

Erwartet wird test_valid_token mit "result": "PASS". Der Teststarter sammelt Funktionen, deren Namen mit test_ beginnen, und ruft jede auf. Er liefert den Exit-Status 0, wenn alle bestehen, 1 bei einer fehlgeschlagenen Assertion und 2 bei einem Test- oder Laufzeitfehler.

Zwei andere Dienste sind absichtlich fehlerhaft: missing-auth akzeptiert Anfragen ohne Token; wrong-auth akzeptiert ein falsches Token. Ihre positive Antwort funktioniert weiterhin. Probiere beide aus:

python3 run_tests.py missing-auth
python3 run_tests.py wrong-auth

Beide bestehen weiterhin den einzigen vorhandenen Test. Dieses Ergebnis zeigt eine Lücke in der Testabdeckung. Der Test hat nie eine abzulehnende Anfrage gesendet und konnte daher keine der beiden Umgehungen bemerken.

Die Anforderung für fehlende Tokens ergänzen

In diesem Schritt ergänzt du einen Test, der die Ablehnung einer echten nicht authentifizierten Anfrage erwartet.

Öffne die Testdatei:

nano test_security.py

Behalte den positiven Test bei und ergänze darunter die neue Funktion. Die vollständige Datei sollte so aussehen:

from api import request

def test_valid_token():
    response = request("/tickets", {"Authorization": "Bearer course_test_fixture_token"})
    assert response["status"] == 200
    assert response["body"] == {"tickets": [{"id": 1, "title": "Printer offline"}]}

def test_missing_token():
    response = request("/tickets", {})
    assert response["status"] == 401
    assert response["body"] == {"error": "unauthorized"}

Die leere Zuordnung {} bedeutet, dass keine Anfrageheader gesendet werden. Die Statusassertion prüft die Ablehnung, während die Assertion zum Antwortinhalt sicherstellt, dass die Fehlerantwort keine Ticketdaten enthält. Behalte die Einrückung mit vier Leerzeichen bei. Speichere mit Ctrl+O, Enter und beende den Editor mit Ctrl+X.

python3 run_tests.py good

Beide Tests sollten bestehen. Verwende jetzt den absichtlich fehlerhaften Dienst für fehlende Header:

python3 run_tests.py missing-auth

Erwartet wird, dass test_missing_token die Angaben "result": "FAIL" und "error": "AssertionError" zeigt. Dieser beabsichtigte Exit-Status 1 ist ein nützlicher Nachweis: Der neue Test erkennt, dass der Dienst eine Anfrage akzeptiert, die er ablehnen sollte. Das ist etwas anderes als ein Importfehler oder ein Fehler bei der Serververbindung.

Die Anforderung für falsche Tokens ergänzen

In diesem Schritt deckst du vorhandene, aber ungültige Zugangsdaten ab.

Ein Header kann vorhanden sein, ohne gültig zu sein. Nur einen fehlenden Header zu testen würde einen Dienst übersehen, der jedes nicht leere Token akzeptiert. Öffne dieselbe Datei und ergänze die dritte Funktion, während du die ersten beiden beibehältst:

nano test_security.py

Verwende diese vollständige Datei:

from api import request

def test_valid_token():
    response = request("/tickets", {"Authorization": "Bearer course_test_fixture_token"})
    assert response["status"] == 200
    assert response["body"] == {"tickets": [{"id": 1, "title": "Printer offline"}]}

def test_missing_token():
    response = request("/tickets", {})
    assert response["status"] == 401
    assert response["body"] == {"error": "unauthorized"}

def test_wrong_token():
    response = request("/tickets", {"Authorization": "Bearer wrong_fixture_token"})
    assert response["status"] == 401
    assert response["body"] == {"error": "unauthorized"}

wrong_fixture_token wird vom korrekten Dienst absichtlich nicht akzeptiert. Speichere und beende den Editor mit Ctrl+O, Enter, Ctrl+X.

python3 run_tests.py good

Erwartet wird, dass alle drei Tests bestehen. Teste dann die Regression mit falschem Header:

python3 run_tests.py wrong-auth

Erwartet wird, dass nur test_wrong_token mit AssertionError fehlschlägt. Ändere 401 nicht zu 200, damit dieser fehlerhafte Dienst besteht; dadurch würdest du die Sicherheitsanforderung entfernen.

Die Erkennung beider Regressionen prüfen

In diesem Schritt prüfst du den vollständigen Testsatz anhand aller drei Implementierungen.

python3 run_tests.py good
python3 run_tests.py missing-auth
python3 run_tests.py wrong-auth

Lies die einzelnen Ergebnisse. Der korrekte Dienst muss alle drei Tests bestehen. Bei der Regression mit fehlendem Header darf nur test_missing_token fehlschlagen, bei der Regression mit falschem Header nur test_wrong_token. Der Geschäftstest mit gültigem Token muss überall weiterhin bestehen.

Diese fehlschlagenden Vergleichsläufe sind beabsichtigt. Ein Syntaxfehler, Verbindungsfehler oder bedingungsloser Fehlschlag weist keine Erkennung nach. Unsere Kursprüfung bestätigt außerdem, dass die Tests die drei echten HTTP-Anfragen an jeden ursprünglichen Dienst gesendet haben.

Die Vergleiche decken diese beiden bekannten Regressionen ab. Sie beweisen nicht jede mögliche Authentifizierungs- oder Autorisierungseigenschaft.

Mit einem erfolgreichen Lauf gegen den korrekten Dienst abschließen

In diesem Schritt schließt du mit einem wiederholbaren Testbefehl für die vorgesehene Implementierung ab.

cat test_security.py

Bestätige, dass der ursprüngliche positive Test und beide Ablehnungstests erhalten sind. Ihre Namen beschreiben die drei unabhängigen Anforderungen.

python3 run_tests.py good

Alle drei Ergebnisse sollten PASS sein. Eine künftige Freigabeprüfung kann denselben Befehl für den vorgesehenen Dienst ausführen. Die absichtlich fehlerhaften Dienste bleiben als Vergleichsbeispiele für die Lehre verfügbar; sie sind nicht die auszuliefernde Implementierung. Du hast echtes Verhalten geschützt, statt eine schriftliche Behauptung zur Testabdeckung abzugeben.

Zusammenfassung

Du hast ausdrückliche Authentifizierungsanforderungen mit drei echten HTTP-Tests verknüpft. Die korrekte Implementierung besteht; jede vorbereitete Umgehung wird durch die zugehörige Ablehnungsassertion erkannt. Dieser Vergleich liefert den Nachweis, dass die Tests Verhalten schützen, statt lediglich ohne Fehler ausgeführt zu werden.