0%

Kapitel 6

Wer was darf, und wie oft

Angemeldet heißt nicht erlaubt

Kapitel 5 hat den Server sicher gemacht, wer fragt. Sicher zu sein, wer jemand ist, sagt noch nichts darüber, was er anfassen darf. Diese Lektion ändert keinen Code. Sie trennt zwei Fragen, die der Server bisher beantwortet hat, als wären sie eine.

Zwei Fragen

Authentifizierung (authentication) fragt „Wer bist du?“. Ein Berechtigungsnachweis, der die Prüfung besteht, beantwortet sie: ein Passwort, ein , ein signiertes Token. Wenn das scheitert, ist die Antwort 401.

Autorisierung (authorization) fragt „Darfst du das tun, mit diesem Ding?“. Eine Regel beantwortet sie, und die Regel muss den Aufrufer, die Aktion und das kennen. Wenn das scheitert, ist die Antwort 403 oder manchmal 404.

with Python hat die Grenze zwischen 401 und 403 mit einer groben Regel gezogen: Darf dieses Token überhaupt schreiben? Eine Anfrage passiert zwei Tore, in dieser Reihenfolge:

  1. „Wer bist du?“ Der Server liest das Token und prüft sein Tag. Kein Token oder eins, das die Prüfung nicht besteht: 401, und die Anfrage endet hier.

  2. „Darfst du das tun, mit diesem Ding?“ Der Server sieht sich den Aufrufer, die und das Buch an. Keine Regel erlaubt es: 403 oder 404, und die Anfrage endet hier.

Nur eine Anfrage, die beide Tore passiert hat, erreicht den Code, der ein Buch liest oder ändert.

Heute sieht sich das zweite Tor den Aufrufer und die Methode an. Das Buch sieht es sich nie an.

Was im Projekt dieses Kapitels neu ist

Das Projekt, das du in der nächsten Lektion öffnest, ist dein Server aus Kapitel 5 mit drei Änderungen, von denen du keine schreiben musst.

  • GET /about ist eine neue, öffentliche Beschreibung des Dienstes. Sie behält das Verhalten mit ETag und max-age, das du in HTTP with Python gebaut hast, auf einer Route, die jeder lesen darf.

  • Die Zugriffsentscheidungen der Bücherrouten stehen jetzt in zwei , collection_refusal für /books und item_refusal für /books/{id}. Sie entscheiden genau so, wie dein Code aus Kapitel 5 es getan hat.

  • handle besteht jetzt aus zwei Zeilen, die eine neue Funktion aufrufen, route, die das ganze Routing enthält. Und request["user"] hält fest, als wer sich eine Anfrage ausgewiesen hat. Lektion 5 verwendet beides.

Auch zwei Tatsachen über die Beispielbenutzer ändern sich. ben darf jetzt schreiben. Und es gibt eine dritte Benutzerin, cleo, mit dem Testpasswort paper lantern, die nur lesen darf.

Lies die Entscheidung

Das ist das mitgelieferte item_refusal. Es gibt None zurück, wenn die Anfrage weitergehen darf, oder die Antwort, die sie ablehnt. claims ist die Payload eines verifizierten Tokens oder None.

def item_refusal(method, claims, book):
    """Return None when this method may go on for this book, or the refusal. Lesson 2 rewrites this."""
    if method == "PUT" or method == "DELETE":
        if claims is None:
            return unauthorized("Bearer")
        if not claims["can_write"]:
            return json_error(403, "this token may read the list but not change it")
    if book is None:
        return json_error(404, "no book has that id")
    if method == "GET" or method == "HEAD" or method == "PUT" or method == "DELETE":
        return None
    return not_allowed("GET, HEAD, PUT, DELETE")

Frag bei jedem , welche Frage es beantwortet. claims is None ist die erste Frage. claims["can_write"] ist eine grobe Form der zweiten: Darf dieses Konto überhaupt etwas schreiben? Keine Zeile fragt, wessen Buch es ist, und keine könnte es, weil ein Buch keinen Eigentümer hat.

Sieh dir auch die Form an. Die Funktion listet auf, was abgelehnt wird, und lässt dann durch, was übrig bleibt.

Das ergibt sich daraus für Anfragen an den Server in seinem jetzigen Zustand. Beide Beispielbücher sollen ana gehören.

AnfrageGesendet vonAntwort jetztRichtig?
DELETE /books/1niemandem, kein Token401ja
POST /bookscleo, die nur lesen darf403ja
DELETE /books/1ben, mit seinem eigenen gültigen Token204, und das Buch ist wegnein
PUT /books/2ben200, und das Buch ist ersetztnein
GET /books/1ben200, mit anas Buchnein
GET /booksniemandem, kein Token200, mit jedem Buchnein

ben hat ein gültiges Token und darf schreiben. Auf dem Server in seinem jetzigen Zustand sendet er DELETE /books/1, und Buch 1 gehört ana. Was gibt item_refusal zurück?

Ein Fremder ohne Token sendet GET /books an den Server in seinem jetzigen Zustand. Was kommt zurück?

Die fehlende Regel

Die alte Regel sieht sich nur das Token an. Die fehlende Regel sieht sich den Benutzer des Tokens und das Buch an. Führ beide für ben und anas Buch aus:

Try it

Es gibt aus:

old rule lets ben delete ana's book: True
owner rule lets ben delete ana's book: False
owner rule lets ana delete it: True

Eine Regel, die das Objekt nennt, heißt Autorisierung auf Objektebene (object-level authorization). Sie wegzulassen ist ein leicht übersehener Fehler, weil für den ehrlichen Benutzer alles weiter funktioniert: ana fragt immer nur nach ihren eigenen Büchern, und ihre Anfragen gelingen so oder so.

Die Eigentümerregel oben prüft user is not None und book is not None, bevor sie irgendetwas vergleicht. Die nächste Frage zeigt, warum.

Was gibt print({}.get("owner") == None) aus?

403 oder 404 für ein Buch, das nicht dir gehört

Wenn ben nach anas Buch fragt, wäre 403 eine ehrliche Antwort. Sie würde auch sagen: Dieses Buch existiert, und es gehört nicht dir. Buch-IDs zählen hier 1, 2, 3. Mit diesem Satz kann ein Fremder aufwärts zählen und kartieren, welche IDs es über alle Leser hinweg gibt.

Deshalb antwortet dieser Dienst mit 404, derselben Antwort, die er für eine ID gibt, die kein Buch hat. Das ist eine Entscheidung dieses Dienstes, und andere Dienste entscheiden sich für 403. Der Preis ist, dass eine 404 einem ehrlichen Client weniger über seinen Fehler verrät.

Die 403, die du in HTTP with Python gebaut hast, bleibt für das Konto, das überhaupt nicht schreiben darf. Diese Antwort hängt von keinem Buch ab, also verrät sie nichts über eines.

Eine ID ist kein Geheimnis, und eine ID, die schwer zu erraten ist, ersetzt die Prüfung nicht.

cleos Token besteht die Prüfung, und ihr Konto darf nur lesen. Sie sendet POST /books. Welche Frage scheitert, und mit welchem Status?

Die nächste Lektion führt bens Anfrage gegen deinen Server aus, und dann schreibst du die Regel.