0%

Kapitel 4

Sessions, die halten

Eine Session-ID ist ein Schlüssel

Nach einer Anmeldung wird das Passwort nicht noch einmal gesendet. Der Browser sendet ein mit einer , und wer diese ID sendet, ist für den Server ana. In with Python hast du die ID mit secrets.token_urlsafe(16) erzeugt, das sind 128 zufällige Bits, also errät niemand eine. In diesem Kapitel geht es um alle anderen Wege, an eine funktionierende Session-ID zu kommen.

Was im Projekt dieses Kapitels neu ist

Das Projekt ist dein Server vom Ende von Kapitel 3 mit einer zusätzlichen . Hier steht, was der neue Code tut und zu welchem Fehler er einlädt.

POST /prefs merkt sich eine Seitengröße für einen Leser, der sich nicht angemeldet hat. Um sich irgendetwas zu merken, braucht der Server eine Session, also gibt diese Route schon vor der Anmeldung eine aus: eine Besucher-Session, deren user None ist.

    elif segments == ["prefs"]:
        if method == "POST":
            response = prefs_error(request)
            if response is None:
                page_size = json_body(request)["page_size"]
                response = json_response(200, {"page_size": page_size})
                session = current_session(request, state)
                if session is None:
                    session_id = start_session(state, None, page_size=page_size)
                    response["headers"]["set-cookie"] = session_cookie(session_id)
                else:
                    session["page_size"] = page_size

Eine Session ist jetzt {"user": ..., "page_size": ...}, und GET /me meldet beides: {"user": null, "page_size": 5} für einen Besucher, der 5 gewählt hat. Mit der Funktion kamen zwei Hilfsfunktionen: session_id_from(request) liest die ID aus dem Cookie, und current_session(request, state) gibt die Session zurück, die diese ID nennt. sample_state() hat außerdem einen Eintrag "clock" für Lektion 3, und drei Funktionen sind Platzhalter, die du in den Lektionen 2, 3 und 5 schreibst.

Der Anmeldezweig musste sich ebenfalls ändern, weil ein Besucher, der sich anmeldet, schon eine Session hat. Nach einem korrekten Passwort führt er jetzt diese vier Zeilen aus:

                session_id = session_id_from(request)
                if session_id is None or session_id not in state["sessions"]:
                    session_id = start_session(state, None)
                state["sessions"][session_id]["user"] = user

Wenn die Anfrage ein Cookie mitbringt, das eine Session nennt, behält die Anmeldung diese Session und schreibt den Benutzer hinein. So überlebt die Seitengröße die Anmeldung.

Lies die vier Zeilen noch einmal und frag dich, wer die Session-ID gewählt hat. Der Server hat sie in diesem Moment nicht gewählt. Die ID kam mit der Anfrage, und der Server hat nur geprüft, dass er die ID kennt. Einer ID, die an einen Besucher ausgegeben wurde, wird dann ein angemeldeter Benutzer anvertraut.

Welche der vier vorgegebenen Zeilen im Anmeldezweig gibt einer ID, die der Server nicht gewählt hat, einen angemeldeten Benutzer?

Der Angriff Schritt für Schritt

Drei Parteien sind beteiligt. Die ID ist auf K7f...Qw gekürzt.

attacker              victim's browser        server
   |                       |                   |
1  |-- POST /prefs --------------------------->|  new session K7f...Qw:
   |<-- set-cookie: session=K7f...Qw ----------|  user None, page_size 5
2  |-- plants K7f...Qw --->|                   |
3  |                       |-- POST /login --->|
   |                       |   cookie K7f...Qw |
   |                       |   ana's password  |
4  |                       |                   |  its user is now "ana"
5  |-- GET /me, cookie: session=K7f...Qw ----->|
   |<-- {"user": "ana", "page_size": 5} -------|
  1. Der Angreifer sendet POST /prefs und bekommt eine Session-ID.

  2. Der Angreifer bringt diese ID in den Browser des Opfers. Der nächste Abschnitt sagt, wie.

  3. Das Opfer meldet sich mit seinem eigenen Passwort an. Sein Browser sendet das untergeschobene Cookie mit.

  4. Der Server findet die Session, die das Cookie nennt, und schreibt ana hinein.

  5. Der Angreifer sendet GET /me mit der ID, die er seit Schritt 1 hat, und der Server antwortet als ana.

Dieselbe ID ist in den Schritten 1, 3 und 5 unterwegs. Der Angreifer hat nie eine ID erraten und nie eine gestohlen. Der Server hat einen aufgewertet, den der Angreifer schon hatte. Dieser Angriff heißt Session-Fixierung (session fixation): Der Angreifer legt die ID vorher fest, und die Anmeldung des Opfers macht sie wertvoll.

Dieser Block spielt den Angriff an einem einfachen durch. Die ID ist absichtlich lesbar; eine echte ist zufällig.

Try it

Er gibt the victim's cookie now holds: id-the-attacker-knows und the attacker asks who that is: ana aus.

Was gibt das aus?

sessions = {"abc": {"user": None}}
sessions["abc"]["user"] = "ana"
print(len(sessions), sessions["abc"]["user"])

Wie kommt die ID in ihren Browser?

Für Schritt 2 gibt es zwei verbreitete Wege.

  • Ein benachbarter Host. Angenommen, die Leseliste liegt unter reading.example und eine Notiz-App unter notes.reading.example. Eine Seite auf dem Notiz-Host kann ein Cookie für die ganze Domain reading.example setzen, und ein Browser sendet dieses Cookie dann auch an die Leseliste. Lektion 4 schließt diesen Weg im Cookie selbst.

  • Ein gemeinsam genutzter Computer. Der Angreifer besucht die Leseliste zuerst, notiert sich die ID und lässt das Cookie zurück. Die nächste Person an diesem Computer meldet sich damit an.

Nichts auf dieser Website ist ein Browser, also beschreibt der Kurs Schritt 2 und zeigt ihn nicht: Ein echter Browser wäre das, was das untergeschobene Cookie trägt. Jeder andere Schritt läuft in der nächsten Lektion gegen deinen Server.

Ein Server, der die Session-ID auch aus der akzeptierte, würde einen dritten Weg hinzufügen, einen Link mit der ID darin. Dieser Server liest die ID nur aus dem Cookie.

Vier Wege, eine Session-ID zu verlieren

Wie die ID verloren gehtWas das bedeutetWo es behandelt wird
ErratenDer Angreifer probiert IDs aus, bis eine funktioniertIn HTTP with Python durch eine ID aus secrets geschlossen
FestgelegtDer Angreifer kennt die ID, bevor sie etwas wert istDieser Angriff; Lektion 2
Unterwegs oder aus einer Seite gelesenEine Kopie entsteht, während das Cookie ohne HTTPS ein Netzwerk durchquert, oder durch ein Skript in einer SeiteLektion 4
Nie in den Ruhestand geschicktEine Kopie funktioniert weiter, weil der Server die Session nie beendetBefund F3; Lektion 3

Eine Session-ID funktioniert noch ein Jahr, nachdem ihr Benutzer sie zuletzt benutzt hat. Welcher der vier Verluste ist das?

Die Reparatur in einem Satz

Wenn sich ändert, was einer Session anvertraut ist, gib ihr eine neue ID. Eine Anmeldung ist so eine Änderung, und die alte ID, die jemand anderes kennen könnte, darf nichts mehr nennen.

Try it

Es gibt the victim's new id is 22 characters the attacker never saw und the attacker asks who the old id is: None aus. Die Seitengröße ist in die neue Session umgezogen, und die alte ID nennt nichts.

Der Angreifer hat weder anas Passwort noch das Cookie gesehen, das ihre Anmeldung zurückgegeben hat. Was wusste er?

In dieser Lektion hat sich an deinem Server nichts geändert. Die nächste Lektion führt den Angriff gegen ihn aus und schließt ihn dort.